Starsector API
Loading...
Searching...
No Matches
ThreatCombatStrategyAI.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat.threat;
2
3import java.util.ArrayList;
4import java.util.LinkedHashSet;
5import java.util.List;
6import java.util.Set;
7
8import org.lwjgl.util.vector.Vector2f;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.combat.AssignmentTargetAPI;
12import com.fs.starfarer.api.combat.BattleObjectiveAPI;
13import com.fs.starfarer.api.combat.CombatAssignmentType;
14import com.fs.starfarer.api.combat.CombatEngineAPI;
15import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
16import com.fs.starfarer.api.combat.CombatFleetManagerAPI.AssignmentInfo;
17import com.fs.starfarer.api.combat.CombatTaskManagerAPI;
18import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
19import com.fs.starfarer.api.combat.ShipAIPlugin;
20import com.fs.starfarer.api.combat.ShipAPI;
21import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags;
22import com.fs.starfarer.api.fleet.FleetGoal;
23import com.fs.starfarer.api.impl.campaign.ids.HullMods;
24import com.fs.starfarer.api.impl.campaign.ids.Tags;
25import com.fs.starfarer.api.util.IntervalUtil;
26import com.fs.starfarer.api.util.Misc;
27
37
38 public static float SND_BASE = 60f;
39 public static float SND_TIMER = 60f;
40 public static float SND_FRACTION = 0.5f;
41
42 protected boolean playerSide;
46 protected int owner;
47 protected boolean allyMode = false;
48
49 protected IntervalUtil everySecond = new IntervalUtil(0.8f, 1.2f);
51 protected float mw, mh;
52
53 protected boolean abort = false;
54
57
58 protected float captureAllTimeRemaining;
59 protected boolean gaveInitialOrders = false;
60
61 protected float untilSNDOnSkirmishUnits;
62
63
66 this.owner = owner;
67 playerSide = owner == 0;
69 //allyMode = false;
72 taskManager.getCommandPointsStat().modifyFlat("ThreatCombatStrategyAI", 1000000000);
73
75
77 abort = true;
78 } else {
79 if (fleetManager.getAdmiralAI() != null) {
82 }
83 }
84
87
89 }
90
91 protected void resetSNDTimer() {
92 untilSNDOnSkirmishUnits = SND_TIMER * (0.75f + (float) Math.random() * 0.5f);
94 }
95
96 protected void manageSND(float amount) {
98 if (captureAllTimeRemaining > 0) return;
99 if (untilSNDOnSkirmishUnits <= 0) {
101 ShipAPI ship = member.getShip();
102 if (ship == null || ship.getAI() == null) continue;
104
105 if (!ship.getHullSpec().hasTag(Tags.THREAT_SKIRMISH)) continue;
106 if ((float) Math.random() > SND_FRACTION) continue;
107
108 cancelOrders(member, false);
109 ship.getAIFlags().setFlag(AIFlags.IGNORES_ORDERS, SND_BASE * (0.75f + (float) Math.random() * 0.5f));
110 }
112 }
113 }
114
115 protected void giveInitialOrders() {
119 }
120 }
121
122
123 public void advance(float amount) {
124 //if (true) return;
125 if (abort) return;
126 if (engine.isPaused()) return;
127
128 captureAllTimeRemaining -= amount;
129
130 manageSND(amount);
131
132 everySecond.advance(amount);
133 if (everySecond.intervalElapsed()) {
134 // if non-threat ships are deployed from this fleetManager, don't want to be doing any Threat things
135 List<DeployedFleetMemberAPI> deployed = fleetManager.getDeployedCopyDFM();
136 if (deployed.isEmpty()) return;
137
138 boolean someMatching = false;
139 for (DeployedFleetMemberAPI member : deployed) {
140 if (!member.isFighterWing() && member.getShip() != null &&
141 member.isAlly() == allyMode &&
142 !member.getShip().getVariant().hasHullMod(HullMods.THREAT_HULLMOD)) {
143 abort = true;
144 return;
145 } else if (!member.isFighterWing() && member.getShip() != null &&
146 member.isAlly() == allyMode) {
147 someMatching = true;
148 }
149 }
150
151 if (!someMatching) return;
152
153 if (!gaveInitialOrders) {
155 gaveInitialOrders = true;
156 }
157
158 float sign = 1f;
159 if (owner == 0) sign = -1;
160 Vector2f enemyCom = getEnemyCenterOfMass();
161 Vector2f fabricatorLoc = new Vector2f(0, 0 + mh * 0.33f * sign);
162 Vector2f axis = Misc.getUnitVector(fabricatorLoc, enemyCom);
163 Vector2f perp = new Vector2f(axis.y, -axis.x);
164 float distToEnemyCom = Misc.getDistance(fabricatorLoc, enemyCom);
165
166 //float hiveOffset = 2000f;
167 float hiveOffset = distToEnemyCom - 6000f;
168 if (Math.abs(hiveOffset) < 2000f) {
169 hiveOffset = Math.signum(hiveOffset) * 2000f;
170 }
171 if (hiveOffset > 2000) hiveOffset= 2000f;
172 Vector2f hiveLoc = new Vector2f(axis);
173 hiveLoc.scale(hiveOffset);
174 Vector2f.add(hiveLoc, fabricatorLoc, hiveLoc);
175
176 float enemyWeightNearFabricatorLoc = Misc.countEnemyWeightInArcAroundLocation(
177 owner, fabricatorLoc, 0f, 360f, 3000f, null, true, true);
178
179 int fabricators = 0;
180 int hives = 0;
182 ShipAPI ship = member.getShip();
183 if (ship == null || ship.getAI() == null) continue;
184 if (isFabricator(ship)) {
185 fabricators++;
186 }
187 if (isHive(ship)) {
188 hives++;
189 }
190 }
191
192 float defDist = distToEnemyCom - 2000f;
193 if (enemyWeightNearFabricatorLoc >= 3f) {
194 defDist = Math.min(defDist, 3000f);
195 }
196 if (fabricators == 0 && hives == 0) {
197 if (mainDefend1 != null) {
199 mainDefend1 = null;
200 }
201 if (mainDefend2 != null) {
203 mainDefend2 = null;
204 }
205 } else if (defDist < 2000f) {
206 if (mainDefend1 != null) {
207 float dist = Misc.getDistance(mainDefend1.getTarget().getLocation(), fabricatorLoc);
208 if (dist > 1000f) {
210 mainDefend1 = null;
211 }
212 }
213
214 if (mainDefend2 != null) {
216 mainDefend2 = null;
217 }
218
219 if (mainDefend1 == null) {
222 }
223
224 //Global.getCombatEngine().getCombatUI().addMessage(0, "Threat aggro mode engaged!");
225 // enemies close to fabricators - attack!
227 ShipAPI ship = member.getShip();
228 if (ship == null || ship.getAI() == null) continue;
229 if (isCombatUnit(ship) && ship.getAI() instanceof ShipAIPlugin) {
230// ShipAIPlugin ai = (ShipAIPlugin) ship.getAI();
231// ShipAIConfig config = ai.getConfig();
232// config.personalityOverride = Personalities.RECKLESS;
233// config.alwaysStrafeOffensively = true;
234// config.backingOffWhileNotVentingAllowed = false;
235// config.turnToFaceWithUndamagedArmor = false;
236// config.burnDriveIgnoreEnemies = true;
237 ship.getAIFlags().setFlag(AIFlags.DO_NOT_BACK_OFF, 2f);
238 ship.getAIFlags().setFlag(AIFlags.DO_NOT_VENT, 2f);
239 ship.getAIFlags().setFlag(AIFlags.IGNORES_ORDERS, 2f);
240 }
241 }
242 } else {
243 Vector2f defLoc = new Vector2f(axis);
244 defLoc.scale(defDist);
245 Vector2f.add(defLoc, fabricatorLoc, defLoc);
246
247 Vector2f defLoc1 = new Vector2f(perp);
248 defLoc1.scale(1000f);
249 Vector2f.add(defLoc1, defLoc, defLoc1);
250
251 Vector2f defLoc2 = new Vector2f(perp);
252 defLoc2.scale(-1000f);
253 Vector2f.add(defLoc2, defLoc, defLoc2);
254
255
256 if (mainDefend1 != null) {
257 float dist = Misc.getDistance(mainDefend1.getTarget().getLocation(), defLoc1);
258 if (dist > 1000f) {
260 mainDefend1 = null;
261 }
262 }
263 if (mainDefend2 != null) {
264 float dist = Misc.getDistance(mainDefend2.getTarget().getLocation(), defLoc2);
265 if (dist > 1000f) {
267 mainDefend2 = null;
268 }
269 }
270
271 if (mainDefend1 == null) {
274 }
275
276 if (mainDefend2 == null) {
279 }
280 }
281
282 if (captureAllTimeRemaining <= 0f) {
283 float axisAngle = Misc.getAngleInDegrees(axis);
284 List<AssignmentTargetAPI> withCaptures = new ArrayList<>();
286 if (info.getTarget() == null) continue;
287 if (info.getType() == CombatAssignmentType.CAPTURE || info.getType() == CombatAssignmentType.CONTROL) {
288 if ((fabricators == 0 && hives == 0) ||
289 !wantsToControl(fabricatorLoc, axisAngle, distToEnemyCom, info.getTarget().getLocation())) {
291 } else {
292 withCaptures.add(info.getTarget());
293 }
294 }
295 }
296
297 if (fabricators > 0 || hives > 0) {
299 if (withCaptures.contains(curr)) continue;
300 if (wantsToControl(fabricatorLoc, axisAngle, distToEnemyCom, curr.getLocation())) {
302 }
303 }
304 }
305 }
306
307 Set<DeployedFleetMemberAPI> escorted = new LinkedHashSet<>();
308
310 ShipAPI ship = member.getShip();
311 if (ship == null || ship.getAI() == null) continue;
313
314 float enemyCheckRange = 3000f;
315 if (isHive(ship)) {
316 enemyCheckRange = 1000f;
317 }
318
319 float enemyWeight = Misc.countEnemyWeightInArcAroundLocation(owner, ship.getLocation(), 0f, 360f, enemyCheckRange, null, true, true);
320 float shipWeight = Misc.getShipWeight(ship, true);
321
322 boolean enemiesNear = enemyWeight >= shipWeight * 0.5f;
323
324 if (isFabricator(ship)) {
325 float min = 0f;
326 float max = 1000f;
327 if (enemiesNear) {
328 min = 1000f;
329 max = 2000f;
330 }
331 giveMovementOrder(member, fabricatorLoc, min, max);
332 } else if (isHive(ship)) {
333 if (enemiesNear) {
334 cancelOrders(member, true);
335 } else {
336 giveMovementOrder(member, hiveLoc, 1000f, 1500f);
337 }
338 } else if (isOverseer(ship)) {
339 DeployedFleetMemberAPI closest = null;
340 float minDist = Float.MAX_VALUE;
342 if (other == member || other.getShip() == null || escorted.contains(other)) continue;
343
344 float extraDistScore = 0f;
345 if (other.getShip().isCruiser() && isCombatUnit(other.getShip())) {
346 extraDistScore = 0f;
347 } else if (other.getShip().isDestroyer() && isCombatUnit(other.getShip())) {
348 extraDistScore = 100000f;
349 } else if (other.getShip().isFrigate() && isCombatUnit(other.getShip())) {
350 if (enemiesNear) {
351 extraDistScore = 500000f;
352 } else {
353 extraDistScore = 1000000f;
354 }
355 } else if (isHive(other.getShip())) {
356 if (enemiesNear) {
357 extraDistScore = 100000f;
358 } else {
359 extraDistScore = 10000000f;
360 }
361 } else {
362 continue;
363 }
364 float dist = Misc.getDistance(member.getLocation(), other.getLocation()) + extraDistScore;
365 if (dist < minDist) {
366 closest = other;
367 minDist = dist;
368 }
369 }
370 if (closest != null) {
371 member.getShip().getAIFlags().setFlag(AIFlags.TIMID_ESCORT, 2f);
372 member.getShip().getAIFlags().setFlag(AIFlags.ESCORT_RANGE_MODIFIER, 2f, 300f);
373 escort(member, closest);
374 }
375 }
376
377 }
378
380 }
381 }
382
383 protected boolean wantsToControl(Vector2f fabricatorLoc, float axisAngle, float distToEnemyCom, Vector2f objectiveLoc) {
384 float dist = Misc.getDistance(fabricatorLoc, objectiveLoc);
385 float angle = Misc.getAngleInDegrees(fabricatorLoc, objectiveLoc);
386 float angleDiff = Misc.getAngleDiff(axisAngle, angle);
387
388 float enemyWeight = Misc.countEnemyWeightInArcAroundLocation(owner, objectiveLoc, 0f, 360f, 3000f, null, true, true);
389 if (enemyWeight <= 0f && angleDiff > 30f) return true;
390 //return !(dist > 2000 && (dist > distToEnemyCom || angleDiff > 45f));
391 return dist < 5000 || (dist < distToEnemyCom && angleDiff < 45f);
392 }
393
394
395 protected void cancelOrders(DeployedFleetMemberAPI member, boolean withSearchAndDestroy) {
397 if (curr != null) {
399 }
400 if (withSearchAndDestroy) {
401 taskManager.orderSearchAndDestroy(member, false);
402 }
403 }
404
405 protected void escort(DeployedFleetMemberAPI member, DeployedFleetMemberAPI target) {
406 if (member.getShip() == null) return;
408 if (curr != null && curr.getType() == CombatAssignmentType.LIGHT_ESCORT &&
409 taskManager.getAssignmentTargetFor(member.getShip()) == target) {
410 return;
411 }
412
415 taskManager.giveAssignment(member, info, false);
416 }
417
418 protected void giveMovementOrder(DeployedFleetMemberAPI member, Vector2f loc, float minDist, float maxDist) {
420 boolean needToMakeAssignment = curr == null || curr.getTarget() == null || Misc.getDistance(curr.getTarget().getLocation(), loc) > 100f;
421
422 float dist = Misc.getDistance(member.getLocation(), loc);
423 if (dist < minDist) {
424 if (curr != null && (curr.getType() == CombatAssignmentType.RALLY_CIVILIAN)) {
426 }
427 }
428
429 boolean needToLeash = false;
430 needToLeash |= curr == null && dist > minDist;
431 needToLeash |= curr != null && dist > maxDist;
432
433 if (needToMakeAssignment && needToLeash) {
436 taskManager.giveAssignment(member, task, false);
437 }
438 }
439
441 taskManager.reassign(); // so assigned members isn't empty
442 List<AssignmentInfo> remove = new ArrayList<>();
444 if (curr.getType() == CombatAssignmentType.CONTROL) continue;
445 if (curr.getType() == CombatAssignmentType.CAPTURE) continue;
446 if (curr.getType() == CombatAssignmentType.DEFEND) continue;
447 if (curr.getAssignedMembers().isEmpty()) {
448 remove.add(curr);
449 }
450 }
451 for (AssignmentInfo curr : remove) {
453 }
454
456 }
457
458 protected Vector2f getEnemyCenterOfMass() {
459 Vector2f com = new Vector2f();
460 float weight = 0;
462 if (member.isFighterWing()) continue;
463 if (member.getShip() == null) continue;
464 if (!engine.isAwareOf(owner, member.getShip())) continue;
465
466 Vector2f.add(member.getLocation(), com, com);
467 weight++; // maybe feels better than scaling weight by ship size etc
468 }
469 if (weight > 0) {
470 com.scale(1f / weight);
471 }
472 return com;
473 }
474
475
476 public static boolean isCombatUnit(ShipAPI ship) {
477 return ship.getHullSpec().hasTag(Tags.THREAT_COMBAT);
478 }
479 public static boolean isOverseer(ShipAPI ship) {
480 return ship.getHullSpec().hasTag(Tags.THREAT_OVERSEER);
481 }
482 public static boolean isHive(ShipAPI ship) {
483 return ship.getHullSpec().hasTag(Tags.THREAT_HIVE);
484 }
485 public static boolean isFabricator(ShipAPI ship) {
487 //return ship.getSystem() != null && ship.getSystem().getId().equals(ShipSystems.CONSTRUCTION_SWARM);
488 }
489}
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
void modifyFlat(String source, float value)
boolean wantsToControl(Vector2f fabricatorLoc, float axisAngle, float distToEnemyCom, Vector2f objectiveLoc)
void cancelOrders(DeployedFleetMemberAPI member, boolean withSearchAndDestroy)
void giveMovementOrder(DeployedFleetMemberAPI member, Vector2f loc, float minDist, float maxDist)
void escort(DeployedFleetMemberAPI member, DeployedFleetMemberAPI target)
static float countEnemyWeightInArcAroundLocation(ShipAPI ship, Vector2f loc, float dir, float arc, float maxRange, ShipAPI ignore, boolean ignoreFightersAndModules)
Definition Misc.java:6820
static float getShipWeight(ShipAPI ship)
Definition Misc.java:6095
static float getAngleDiff(float from, float to)
Definition Misc.java:1716
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static Vector2f getUnitVector(Vector2f from, Vector2f to)
Definition Misc.java:1191
static float getAngleInDegrees(Vector2f v)
Definition Misc.java:1126
default void setNoOrders(boolean noOrders)
boolean isAwareOf(int owner, CombatEntityAPI other)
CombatFleetManagerAPI getFleetManager(FleetSide side)
List< BattleObjectiveAPI > getObjectives()
List< DeployedFleetMemberAPI > getDeployedCopyDFM()
CombatTaskManagerAPI getTaskManager(boolean ally)
AssignmentTargetAPI createWaypoint2(Vector2f loc, boolean ally)
AssignmentInfo createAssignment(CombatAssignmentType type, AssignmentTargetAPI target, boolean useCommandPoint)
AssignmentInfo getAssignmentFor(ShipAPI ship)
void setAssignmentWeight(AssignmentInfo info, float weight)
AssignmentTargetAPI getAssignmentTargetFor(ShipAPI ship)
void giveAssignment(DeployedFleetMemberAPI member, AssignmentInfo assignment, boolean useCommandPointIfNeeded)
void orderSearchAndDestroy(DeployedFleetMemberAPI member, boolean useCommandPointIfNeeded)