Starsector API
Loading...
Searching...
No Matches
ThreatSwarmAI.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat.threat;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import org.lwjgl.util.vector.Vector2f;
7
8import com.fs.starfarer.api.Global;
9import com.fs.starfarer.api.combat.CombatEngineAPI;
10import com.fs.starfarer.api.combat.DamageType;
11import com.fs.starfarer.api.combat.FighterWingAPI;
12import com.fs.starfarer.api.combat.MutableShipStatsAPI;
13import com.fs.starfarer.api.combat.ShipAIConfig;
14import com.fs.starfarer.api.combat.ShipAIPlugin;
15import com.fs.starfarer.api.combat.ShipAPI;
16import com.fs.starfarer.api.combat.ShipCommand;
17import com.fs.starfarer.api.combat.ShipwideAIFlags;
18import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags;
19import com.fs.starfarer.api.combat.WeaponGroupAPI;
20import com.fs.starfarer.api.impl.combat.threat.ConstructionSwarmSystemScript.SwarmConstructionData;
21import com.fs.starfarer.api.util.IntervalUtil;
22import com.fs.starfarer.api.util.Misc;
23import com.fs.starfarer.api.util.WeightedRandomPicker;
24
25public class ThreatSwarmAI implements ShipAIPlugin {
26
27 public static float ATTRACTOR_RANGE_MAX_SAME_WING = 1000000f;
28 public static float ATTRACTOR_RANGE_MAX = 500f;
29 public static float COHESION_RANGE_MIN = 150f;
30 public static float COHESION_RANGE_MAX = 300f;
31 public static float REPEL_RANGE_MIN = 0f;
32 public static float REPEL_RANGE_MAX = 150f;
33
34 public static float MAX_TARGET_RANGE = 3000f;
35
36 public static boolean isAttackSwarm(ShipAPI ship) {
38 }
42 public static boolean isReclamationSwarm(ShipAPI ship) {
44 }
45
46 public static class SharedSwarmWingData {
47 public ShipAPI target = null;
48 }
49
50 public static class FlockingData {
51 public Vector2f loc;
52 public Vector2f vel;
53 public float minA;
54 public float maxA;
55 public float minR;
56 public float maxR;
57 public float repelAtAngleDist;
58 public float minC;
59 public float maxC;
60 public float attractWeight;
61 public float repelWeight;
62 public float cohesionWeight;
63 public float facing;
64 }
65
66 public static float PROB_ENABLE_OTHER_GROUP = 0.5f;
67
69 protected ShipAPI ship;
70
71 protected IntervalUtil updateInterval = new IntervalUtil(0.5f, 1.5f);
72 protected IntervalUtil headingInterval = new IntervalUtil(0.5f, 1.5f);
75
76 protected float sinceTurnedOffFlash = 0f;
77 protected ShipAPI fabricator = null;
78
79 protected List<FlockingData> flockingData = new ArrayList<>();
80 protected float desiredHeading = 0f;
81 protected float headingChangeRate = 0f;
82 protected float elapsedSincePrevHeadingUpdate = 0f;
83 protected float attackRangeMult = 1f;
84
87 protected float enableOtherWeaponDuration = 0f;
88 protected float elapsed = 0f;
89
90 protected boolean startedConstruction = false;
91 private SwarmConstructionData constructionData;
92 private ThreatShipConstructionScript constructionScript;
93
94 protected boolean attackSwarm = false;
95 protected boolean constructionSwarm = false;
96 protected boolean reclamationSwarm = false;
97
112
113 public SharedSwarmWingData getShared() {
114 if (ship.getWing() == null) return new SharedSwarmWingData();
115
116 String key = "SharedSwarmWingData";
117 SharedSwarmWingData data = (SharedSwarmWingData) ship.getWing().getCustomData().get(key);
118 if (data == null) {
119 data = new SharedSwarmWingData();
120 ship.getWing().getCustomData().put(key, data);
121 }
122 return data;
123 }
124
125 protected void doInitialSetup() {
126 if (attackSwarm) {
127 // 0: voltaic
128 // 1: unstable
129 // 2: kinetic
130 // 3: seeker
131 // 4: defabrication
132
133 toggleOn(0);
134 toggleOn(1);
135 toggleOff(2);
136 toggleOff(3);
137 toggleOff(4);
138
140 }
141 }
142
143 protected void toggleOn(int groupNum) {
144 List<WeaponGroupAPI> groups = ship.getWeaponGroupsCopy();
145 if (groups.size() <= groupNum) return;
146 groups.get(groupNum).toggleOn();
147 }
148 protected void toggleOff(int groupNum) {
149 List<WeaponGroupAPI> groups = ship.getWeaponGroupsCopy();
150 if (groups.size() <= groupNum) return;
151 groups.get(groupNum).toggleOff();
152 }
153
154
155 protected void advanceForSpecificSwarmType(float amount) {
156 if (attackSwarm) {
157 if (ship.isWingLeader()) {
159 if (priorityTargetPickerInterval.intervalElapsed()) {
161 }
162 }
163
164 attackRangeMultInterval.advance(amount * 0.1f);
165 if (attackRangeMultInterval.intervalElapsed()) {
167 }
168
169 // 0: voltaic, always on
170 // 1: unstable, always on
171 // 2: kinetic
172 // 3: seeker
173 // 4: unused (was defab at some point)
176 if (enableOtherWeaponDuration <= 0) {
177 toggleOff(2);
178 toggleOff(3);
179 toggleOff(4);
180 }
181 } else {
182 //amount *= 10f;
184
185
187 if (enableOtherWeaponInterval.intervalElapsed()) {
188 if ((float) Math.random() < PROB_ENABLE_OTHER_GROUP) {
189 toggleOff(2);
190 toggleOff(3);
191 toggleOff(4);
192
193 ShipAPI target = (ShipAPI) flags.getCustom(AIFlags.MANEUVER_TARGET);
194
195// boolean targetShieldsFacingUs = false;
196// if (target != null) {
197// ShieldAPI targetShield = target.getShield();
198// targetShieldsFacingUs = targetShield != null &&
199// targetShield.isOn() &&
200// Misc.isInArc(targetShield.getFacing(), Math.max(30f, targetShield.getActiveArc()),
201// target.getLocation(), ship.getLocation());
202// }
203// if (targetShieldsFacingUs) {
204// toggleOn(2);
205// } else {
206// toggleOn(3);
207// }
208
209
210 // use Seeker only when it will be destroyed by using seeker, as a "final attack"
211 boolean useSeeker = ship.getHullLevel() < 0.22f;
212 boolean useKinetic = true;
213 if (target == null || target.isFighter()) {
214 useSeeker = false;
215 useKinetic = false;
216 }
217
218 if (useSeeker) {
219 toggleOn(3);
220 } else if (useKinetic) {
221 toggleOn(2);
222 }
223
224 enableOtherWeaponDuration = 0.5f + 0.5f * (float) Math.random();
225 }
226 }
227 }
228 }
229
230 if (constructionSwarm) {
231 if (constructionData == null) {
233 if (swarm != null) {
234 constructionData = (SwarmConstructionData) swarm.custom1;
235 }
236 }
237 if (constructionData != null) {
238 if (elapsed > constructionData.preConstructionTravelTime && !startedConstruction) {
239 startedConstruction = true;
241 if (swarm != null) {
242 constructionScript = new ThreatShipConstructionScript(
243 constructionData.variantId, ship, 0f, constructionData.constructionTime);
244 Global.getCombatEngine().addPlugin(constructionScript);
245 }
246 }
247 }
248 }
249
250 if (reclamationSwarm) {
251 if (fabricator == null) {
253 if (reclamationReturnInterval.intervalElapsed()) {
254 int owner = ship.getOriginalOwner();
256 for (ShipAPI curr : engine.getShips()) {
257 if (curr == ship || curr.getOwner() != owner) continue;
258 if (curr.isHulk() || curr.getOwner() == 100) continue;
259 if (!ThreatCombatStrategyAI.isFabricator(curr)) continue;
260
261 float dist = Misc.getDistance(curr.getLocation(), ship.getLocation());
262 if (dist < curr.getCollisionRadius() + 200f) {
263 // turn off flash and return to fabricator
265 swarm.params.flashFrequency = 0f;
266 swarm.params.flashProbability = 0f;
267
268 fabricator = curr;
269 break;
270 }
271 }
272 }
273 } else {
274 sinceTurnedOffFlash += amount;
275 if (sinceTurnedOffFlash > 3f) {
277 if (fabricator.isAlive()) {
278 fabricator.setCurrentCR(Math.min(1f, fabricator.getCurrentCR() + 0.01f * ship.getHullLevel()));
279
282 if (swarm != null && swarmFabricator != null) {
283 swarm.transferMembersTo(swarmFabricator, swarm.getNumActiveMembers());
284 }
285 }
286 ship.setHitpoints(0f);
287 ship.setSpawnDebris(false);
288 engine.applyDamage(ship, ship.getLocation(), 100f, DamageType.ENERGY, 0f, true, false, ship, false);
289 }
290 }
291 }
292 }
293
294 protected void pickPriorityTarget() {
295 SharedSwarmWingData data = getShared();
296 if (data.target != null && data.target.isAlive()) {
297 return;
298 }
299
302 int owner = ship.getOriginalOwner();
303
304 for (ShipAPI curr : engine.getShips()) {
305 if (curr == ship) continue;
306 if (curr.isFighter()) continue;
307 if (curr.isHulk() || curr.getOwner() == 100) continue;
308
309 if (curr.getOwner() != owner && engine.isAwareOf(owner, curr)) {
310 float weight = getShipWeight(curr);
311 if (curr.isFrigate()) {
312 weight *= 0.0001f;
313 }
314 picker.add(curr, weight);
315 }
316 }
317
318 data.target = picker.pick();
319 }
320 protected void updateAttackRangeMult() {
321 //attackRangeMult = 0.75f + 0.5f * (float) Math.random();
322 attackRangeMult = 0.5f + 1f * (float) Math.random();
323 }
324
325 @Override
326 public void advance(float amount) {
327 //if (true) return;
328
329 elapsed += amount;
331
332 updateInterval.advance(amount);
333 if (updateInterval.intervalElapsed()) {
335 }
336
337 headingInterval.advance(amount * 5f);
338 if (headingInterval.intervalElapsed()) {
341 }
342
344
346 }
347
348 protected void giveMovementCommands() {
349 if (constructionScript != null && constructionScript.getShip() != null) {
351 return;
352 }
353
354 String source = "swarm_wingman_catch_up_speed_bonus";
356 if (ship.isWingLeader() || ship.getWingLeader() == null) {
357 stats.getMaxSpeed().unmodifyMult(source);
358 stats.getAcceleration().unmodifyMult(source);
359 stats.getDeceleration().unmodifyMult(source);
360 } else {
361 ShipAPI leader = ship.getWingLeader();
362 float dist = Misc.getDistance(ship.getLocation(), leader.getLocation());
363 float mult = (dist - COHESION_RANGE_MAX * 0.5f -
364 ship.getCollisionRadius() * 0.5f - leader.getCollisionRadius() * 0.5f) / COHESION_RANGE_MAX;
365 if (mult < 0f) mult = 0f;
366 if (mult > 1f) mult = 1f;
367 stats.getMaxSpeed().modifyMult(source, 1f + .25f * mult);
368 stats.getAcceleration().modifyMult(source, 1f + 0.5f * mult);
369 stats.getDeceleration().modifyMult(source, 1f + 0.5f * mult);
370 }
371
372 float useHeading = desiredHeading;
373 //useHeading += headingChangeRate * elapsedSincePrevHeadingUpdate;
374
376 engine.headInDirectionWithoutTurning(ship, useHeading, 10000);
377 Misc.turnTowardsFacingV2(ship, useHeading, 0f);
378 }
379
380 protected void computeDesiredHeading() {
381
382 Vector2f loc = ship.getLocation();
383 Vector2f vel = ship.getVelocity();
384 float facing = ship.getFacing();
385
386 Vector2f total = new Vector2f();
387
388 for (FlockingData curr : flockingData) {
389 float dist = Misc.getDistance(curr.loc, loc);
390 if (curr.maxR > 0 && dist < curr.maxR) {
391 float repelWeight = curr.repelWeight;
392 if (dist > curr.minR && curr.maxR > curr.minR) {
393 repelWeight = (dist - curr.minR) / (curr.maxR - curr.minR);
394 if (repelWeight > 1f) repelWeight = 1f;
395 repelWeight = 1f - repelWeight;
396 repelWeight *= curr.repelWeight;
397 }
398
399 Vector2f dir = Misc.getUnitVector(curr.loc, loc);
400
401 float distIntoRepel = curr.maxR - dist;
402 float repelAdjustmentAngle = 0f;
403 if (distIntoRepel < curr.repelAtAngleDist && curr.repelAtAngleDist > 0) {
404 float repelMult = (1f - distIntoRepel / curr.repelAtAngleDist);
405 repelAdjustmentAngle = 90f * repelMult;
406 repelWeight *= (1f - repelMult);
407
408 float repelAngle = Misc.getAngleInDegrees(dir);
409 float turnDir = Misc.getClosestTurnDirection(dir, vel);
410 repelAdjustmentAngle *= turnDir;
411 dir = Misc.getUnitVectorAtDegreeAngle(repelAngle + repelAdjustmentAngle);
412 }
413
414 dir.scale(repelWeight);
415 Vector2f.add(total, dir, total);
416 }
417
418 if (curr.maxA > 0 && dist < curr.maxA) {
419 float attractWeight = curr.attractWeight;
420 if (dist > curr.minA && curr.maxA > curr.minA) {
421 attractWeight = (dist - curr.minA) / (curr.maxA - curr.minA);
422 if (attractWeight > 1f) attractWeight = 1f;
423 attractWeight = 1f - attractWeight;
424 attractWeight *= curr.attractWeight;
425 }
426
427 Vector2f dir = Misc.getUnitVector(loc, curr.loc);
428 dir.scale(attractWeight);
429 Vector2f.add(total, dir, total);
430 }
431
432 if (curr.maxC > 0 && dist < curr.maxC) {
433 float cohesionWeight = curr.cohesionWeight;
434 if (dist > curr.minC && curr.maxC > curr.minC) {
435 cohesionWeight = (dist - curr.minC) / (curr.maxC - curr.minC);
436 if (cohesionWeight > 1f) cohesionWeight = 1f;
437 cohesionWeight = 1f - cohesionWeight;
438 cohesionWeight *= curr.cohesionWeight;
439 }
440
441 Vector2f dir = new Vector2f(curr.vel);
442 Misc.normalise(dir);
443 dir.scale(cohesionWeight);
444 Vector2f.add(total, dir, total);
445 }
446 }
447
448 if (total.length() <= 0) {
451 } else {
452// Vector2f currDir = new Vector2f(vel);
453// Misc.normalise(currDir);
454// currDir.scale(total.length() * 0.25f);
455// Vector2f.add(total, currDir, total);
456
457 float prev = desiredHeading;
461 } else {
463 }
464 }
465 }
466
467
468 protected void updateFlockingData() {
469 flockingData.clear();
470
472
473 if (constructionScript != null && constructionScript.getShip() != null) {
474 return;
475 }
476
477
478 int owner = ship.getOriginalOwner();
479 FighterWingAPI wing = ship.getWing();
480 if (wing == null) return;
481
482 Vector2f loc = ship.getLocation();
483 boolean wingLeader = ship.isWingLeader();
484
485 float attackRange = wing.getSpec().getAttackRunRange();
486 attackRange *= attackRangeMult;
487 float radius = ship.getCollisionRadius() * 0.5f;
488
490 if (swarm != null) { // && (constructionSwarm) {
491 radius = swarm.params.maxOffset;
492 }
493
494 SharedSwarmWingData shared = getShared();
495
496 ShipAPI target = null;
497 float targetDist = Float.MAX_VALUE;
498
499 for (ShipAPI curr : engine.getShips()) {
500 if (curr == ship) continue;
501 if (curr.isFighter() && (!curr.isWingLeader() || curr.getOwner() == owner || !attackSwarm)) continue;
502
503 // just avoid everything - looking for a clear area
504 if (constructionSwarm) {
505 float currRadius = curr.getCollisionRadius() * 2f;
506 FlockingData data = new FlockingData();
507 data.facing = curr.getFacing();
508 data.loc = curr.getLocation();
509 data.vel = curr.getVelocity();
510 data.attractWeight = 0f;
511 data.repelWeight = getShipWeight(curr) * 1f;
512 data.minA = 0f;
513 data.maxA = 0f;
514 data.minR = radius + currRadius;
515 data.maxR = radius + currRadius + Math.min(100f, currRadius * 1f);
516 data.repelAtAngleDist = (data.maxR - data.minR) * 0.5f;
517 flockingData.add(data);
518 continue;
519 }
520
521 if (curr.isHulk() || curr.getOwner() == 100) continue;
522
523 // return to Fabricator Units, ignore other ships
524 if (reclamationSwarm) {
525 if (!ThreatCombatStrategyAI.isFabricator(curr) || curr.getOwner() != owner) continue;
526 float currRadius = curr.getCollisionRadius() * 0.5f;
527 FlockingData data = new FlockingData();
528 data.facing = curr.getFacing();
529 data.loc = curr.getLocation();
530 data.vel = curr.getVelocity();
531 data.attractWeight = getShipWeight(curr) * (1f - ship.getCurrentCR());
532 data.repelWeight = data.attractWeight * 10f;
533 data.minA = radius + currRadius;
534 data.maxA = 1000000f;
535 data.minR = radius + currRadius;
536 data.maxR = radius + currRadius + 100f;
537 data.repelAtAngleDist = (data.maxR - data.minR) * 0.5f;
538 flockingData.add(data);
539 continue;
540 }
541
542
543 float currRadius = curr.getCollisionRadius() * 0.5f;
544
545 if (curr.getOwner() != owner && engine.isAwareOf(owner, curr)) {
546 FlockingData data = new FlockingData();
547 data.facing = curr.getFacing();
548 data.loc = curr.getLocation();
549 data.vel = curr.getVelocity();
550 data.attractWeight = getShipWeight(curr);
551 data.repelWeight = data.attractWeight * 10f;
552 data.minA = attackRange + radius + currRadius;
553 data.maxA = 1000000f;
554 data.repelAtAngleDist = Math.min(attackRange * 0.5f, 400f);
555 data.minR = radius + currRadius;
556 data.maxR = attackRange + radius + currRadius;
557 if (curr == shared.target) {
558 //boolean inFront = Misc.isInArc(curr.getFacing(), 90f, curr.getLocation(), ship.getLocation());
559 float angleDiffFromFront = Misc.getAngleDiff(curr.getFacing(), Misc.getAngleInDegrees(curr.getLocation(), ship.getLocation()));
560 float maxDiff = 45f;
561 if (angleDiffFromFront < maxDiff) {
562// data.minR *= 2f - angleDiffFromFront / maxDiff;
563// data.maxR *= 2f - angleDiffFromFront / maxDiff;
564 data.minR = data.minR + (data.maxR - data.minR) * 0.5f;
565 data.minR += 500f * (1f - angleDiffFromFront / maxDiff);
566 data.maxR += 500f * (1f - angleDiffFromFront / maxDiff);
567 data.repelAtAngleDist += 500f * (1f - angleDiffFromFront / maxDiff);
568 }
569 data.attractWeight += 200f;
570 data.repelWeight += 600f;
571 }
572 flockingData.add(data);
573
574 float dist = Misc.getDistance(loc, curr.getLocation());
575 if (dist < targetDist && dist < MAX_TARGET_RANGE) {
576 target = curr;
577 targetDist = dist;
578 }
579
580 // add extra attractor behind the enemy ship to encourage going around and not hanging out in one spot
581 if ((curr.isDestroyer() || curr.isCruiser() || curr.isCapital()) && curr == shared.target) {
582 data = new FlockingData();
583 //Vector2f dir = Misc.getUnitVector(ship.getLocation(), curr.getLocation());
584 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(curr.getFacing() + 180f);
585 dir.scale(curr.getCollisionRadius() * 0.5f + attackRange * attackRangeMult);
586 data.facing = curr.getFacing();
587 data.loc = Vector2f.add(curr.getLocation(), dir, new Vector2f());
588 data.vel = curr.getVelocity();
589 data.attractWeight = getShipWeight(curr) * 1f;
590 data.minA = attackRange + radius + currRadius;
591 data.maxA = 1000000f;
592 if (curr == shared.target) {
593 data.attractWeight += 200f;
594 }
595 flockingData.add(data);
596 }
597
598
599 } else if (curr.getOwner() == owner) {
600 FlockingData data = new FlockingData();
601 data.facing = curr.getFacing();
602 data.loc = curr.getLocation();
603 data.vel = curr.getVelocity();
604 data.attractWeight = getShipWeight(curr) * 0.1f;
605 data.repelWeight = data.attractWeight * 50f;
606 data.minA = attackRange + radius + currRadius;
607 data.maxA = 1000000f;
608 data.minR = radius + currRadius;
609 data.maxR = attackRange * 0.75f + radius + currRadius;
610 data.repelAtAngleDist = Math.min(attackRange * 0.5f, 400f);
611 flockingData.add(data);
612 }
613 }
614
615 if (target != null) {
616 flags.setFlag(AIFlags.MANEUVER_TARGET, 3f, target);
617 } else {
618 flags.unsetFlag(AIFlags.MANEUVER_TARGET);
619 }
620
621 if (flockingData.isEmpty() && !constructionSwarm) {
622 FlockingData data = new FlockingData();
623 data.facing = 0f;
624 data.loc = new Vector2f();
625 data.vel = new Vector2f();
626 data.attractWeight = 5f;
627 data.repelWeight = data.attractWeight * 10f;
628 data.minA = 1000f;
629 data.maxA = 1000000f;
630 data.minR = 1000f;
631 data.maxR = 3000f;
632 data.repelAtAngleDist = 1000f;
633 flockingData.add(data);
634 }
635
636// if (true) {
637// FlockingData data = new FlockingData();
638// data.facing = 0f;
639// data.loc = new Vector2f(8000f, -18000f);
640// data.vel = new Vector2f();
641// data.attractWeight = 1000f;
642// data.minA = 1000f;
643// data.maxA = 1000000f;
644// flockingData.add(data);
645// }
646
647 //RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
648 if (swarm != null && swarm.params.flockingClass != null && swarm.attachedTo != null) {
649 for (RoilingSwarmEffect curr : RoilingSwarmEffect.getFlockingMap().getList(swarm.params.flockingClass)) {
650 if (curr == swarm) continue;
651 if (curr.attachedTo == ship || curr.attachedTo == null ||
652 curr.attachedTo.getOwner() != owner) {
653 continue;
654 }
655
656 if (swarm.params.flockingClass.equals(curr.params.flockingClass)) {
657 // avoid other construction swarms - looking for a clear area
658 if (constructionSwarm) {
659 float currRadius = curr.params.maxOffset;
660 FlockingData data = new FlockingData();
661 data.facing = curr.attachedTo.getFacing();
662 data.loc = curr.attachedTo.getLocation();
663 data.vel = curr.attachedTo.getVelocity();
664 data.attractWeight = 0f;
665 data.repelWeight = 8f;
666 data.minA = 0f;
667 data.maxA = 0f;
668 data.minR = radius + currRadius;
669 data.maxR = radius + currRadius + Math.min(100f, currRadius * 1f);
670 data.repelAtAngleDist = (data.maxR - data.minR) * 0.5f;
671 flockingData.add(data);
672 continue;
673 }
674
675
676 boolean sameWing = wing == ((ShipAPI)curr.attachedTo).getWing();
677 boolean otherWingLeader = ((ShipAPI)curr.attachedTo).isWingLeader();
678
679 // actually - make the leader wait a bit, otherwise they never catch up
680 // or not
681 if (wingLeader && sameWing) continue; // others catch up/line up on leader
682
683 if (!sameWing) {
684 float dist = Misc.getDistance(loc, curr.attachedTo.getLocation());
685 if (dist > ATTRACTOR_RANGE_MAX + 500f) continue;
686 }
687
688
689 float currRadius = curr.attachedTo.getCollisionRadius() * 0.5f;
690 FlockingData data = new FlockingData();
691 data.facing = curr.attachedTo.getFacing();
692 data.loc = curr.attachedTo.getLocation();
693 data.vel = curr.attachedTo.getVelocity();
694 data.attractWeight = 1f;
695 data.repelWeight = 10f;
696 data.cohesionWeight = 1f;
697 if (sameWing) {
698 if (wingLeader) {
699 data.attractWeight = 0.1f;
700 } else {
701 data.attractWeight = 3f;
702 }
703 data.minA = 0f + radius + currRadius;
704 data.maxA = ATTRACTOR_RANGE_MAX_SAME_WING + radius + currRadius;
705 } else {
706 data.minA = 0f + radius + currRadius;
707 data.maxA = ATTRACTOR_RANGE_MAX + radius + currRadius;
708 }
709 data.minR = REPEL_RANGE_MIN + radius + currRadius;
710 data.maxR = REPEL_RANGE_MAX + radius + currRadius;
711 if (wingLeader && otherWingLeader) {
712 data.maxR = ATTRACTOR_RANGE_MAX + radius + currRadius;
713 }
714 data.minC = COHESION_RANGE_MIN + radius + currRadius;
715 data.maxC = COHESION_RANGE_MAX + radius + currRadius;
716 if (reclamationSwarm) {
717 data.minR *= 0.33f;
718 data.maxR *= 0.33f;
719 }
720 flockingData.add(data);
721 }
722 }
723 }
724 }
725
726
727
728
729
730
731
732 @Override
734 return flags;
735 }
736
737 public static float getShipWeight(ShipAPI ship) {
738 return getShipWeight(ship, true);
739 }
740 public static float getShipWeight(ShipAPI ship, boolean adjustForNonCombat) {
741 boolean nonCombat = ship.isNonCombat(false);
742 float weight = 0;
743 switch (ship.getHullSize()) {
744 case CAPITAL_SHIP: weight += 8; break;
745 case CRUISER: weight += 4; break;
746 case DESTROYER: weight += 2; break;
747 case FRIGATE: weight += 1; break;
748 case FIGHTER: weight += 1; break;
749 }
750 if (nonCombat && adjustForNonCombat) weight *= 0.25f;
751 if (ship.isDrone()) weight *= 0.1f;
752 return weight;
753 }
754
755 public void setDoNotFireDelay(float amount) {}
757 public boolean needsRefit() { return false; }
758 public void cancelCurrentManeuver() {}
759 public ShipAIConfig getConfig() { return null; }
760}
761
762
763
764
765
766
767
768
769
770
771
772
773
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
void modifyMult(String source, float value)
void transferMembersTo(RoilingSwarmEffect other, float fraction)
static RoilingSwarmEffect getSwarmFor(CombatEntityAPI entity)
static float getShipWeight(ShipAPI ship, boolean adjustForNonCombat)
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
Definition Misc.java:1196
static boolean turnTowardsFacingV2(MissileAPI missile, float desiredFacing, float relativeAngVel)
Definition Misc.java:6551
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 getClosestTurnDirection(float facing, float desired)
Definition Misc.java:2102
static float getAngleInDegrees(Vector2f v)
Definition Misc.java:1126
static Vector2f normalise(Vector2f v)
Definition Misc.java:1134
boolean isAwareOf(int owner, CombatEntityAPI other)
void applyDamage(CombatEntityAPI entity, Vector2f point, float damageAmount, DamageType damageType, float empAmount, boolean bypassShields, boolean dealsSoftFlux, Object source, boolean playSound)
void headInDirectionWithoutTurning(MissileAPI missile, float desiredHeading, float desiredSpeed)
void addPlugin(EveryFrameCombatPlugin plugin)
boolean isNonCombat(boolean considerOrders)
void setSpawnDebris(boolean spawnDebris)
MutableShipStatsAPI getMutableStats()
List< WeaponGroupAPI > getWeaponGroupsCopy()
void giveCommand(ShipCommand command, Object param, int groupNumber)