001package com.fs.starfarer.api.impl.combat.threat;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.lwjgl.util.vector.Vector2f;
007
008import com.fs.starfarer.api.Global;
009import com.fs.starfarer.api.combat.BoundsAPI.SegmentAPI;
010import com.fs.starfarer.api.combat.CombatEngineAPI;
011import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
012import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
013import com.fs.starfarer.api.combat.MutableShipStatsAPI;
014import com.fs.starfarer.api.combat.ShipAPI;
015import com.fs.starfarer.api.combat.ShipAPI.HullSize;
016import com.fs.starfarer.api.combat.ShipSystemAPI;
017import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState;
018import com.fs.starfarer.api.combat.ShipVariantAPI;
019import com.fs.starfarer.api.impl.campaign.ids.Tags;
020import com.fs.starfarer.api.impl.combat.BaseShipSystemScript;
021import com.fs.starfarer.api.loading.WeaponSlotAPI;
022import com.fs.starfarer.api.util.CountingMap;
023import com.fs.starfarer.api.util.Misc;
024import com.fs.starfarer.api.util.WeightedRandomPicker;
025
026public class ConstructionSwarmSystemScript extends BaseShipSystemScript {
027        
028
029//      public static float MIN_COOLDOWN = 2f;
030//      public static float MAX_COOLDOWN = 10f;
031//      public static float COOLDOWN_DP_MULT = 0.25f;
032        public static int BASE_FRAGMENTS = 50;
033        
034        public static float CONSTRUCTION_SWARM_SPEED_MULT = 0.33f;
035//      public static float BASE_CONSTRUCTION_TIME = 2f;
036//      public static float CONSTRUCTION_TIME_DP_MULT = 1f;
037        public static float BASE_CONSTRUCTION_TIME = 5f;
038        public static float CONSTRUCTION_TIME_DP_MULT = 1.5f;
039        public static float CONSTRUCTION_TIME_OVERSEER_EXTRA = 13f; // makes it 30 seconds
040        
041        public static float NUM_LARGE_AS_FRACTION_OF_DESTROYERS = 0.5f;
042        public static float NUM_DESTROYERS_AS_FRACTION_OF_FRIGATES = 0.6f;
043        
044        public static int FAST_CONSTRUCTION_FRIGATES_MAX = 2;
045        
046        public static float MIN_CR;
047        public static float MIN_DP;
048        public static int MIN_FRAGMENTS;
049        public static int MAX_FRAGMENTS;
050        
051        
052        
053        public static enum SwarmConstructableType {
054                COMBAT_UNIT,
055                OVERSEER,
056                HIVE,
057        }
058        
059        public static class SwarmConstructableVariant {
060                public SwarmConstructableType type;
061                public String variantId;
062                public float cr; // 0 to 1 (so, 0.02 or similar)
063                public float dp;
064                public int fragments;
065                public HullSize size;
066                
067                public SwarmConstructableVariant(SwarmConstructableType type, String variantId) {
068                        this.type = type;
069                        this.variantId = variantId;
070                        
071                        ShipVariantAPI v = Global.getSettings().getVariant(variantId);
072                        dp = v.getHullSpec().getSuppliesToRecover();
073                        size = v.getHullSize();
074                        
075                        cr = 0.01f;
076                        if (v.getHullSize() == HullSize.FRIGATE) {
077                                cr = 0.02f;
078                        } else if (v.getHullSize() == HullSize.DESTROYER) {
079                                cr = 0.04f;
080                        } else if (v.getHullSize() == HullSize.CRUISER) {
081                                cr = 0.06f;
082                        } else if (v.getHullSize() == HullSize.CAPITAL_SHIP) {
083                                cr = 0.1f;
084                        }
085                        
086                        if (type == SwarmConstructableType.HIVE) {
087                                cr += 0.02f;
088                        }
089                        if (type == SwarmConstructableType.OVERSEER) {
090                                cr += 0.01f;
091                        }
092                        fragments = getFragmentCost(dp, size);
093                }
094        }
095        
096        public static List<SwarmConstructableVariant> CONSTRUCTABLE = new ArrayList<>();
097        
098        protected static boolean inited = false;
099        /**
100         * Can't do this in a static block because the AI script is loaded and references this
101         * and would run the static block which in turns triggers some stuff that makes the game crash on startup.
102         */
103        public static void init() {
104                if (inited) return;
105                inited = true;
106                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "skirmish_unit_Type100"));
107                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "skirmish_unit_Type101"));
108                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "assault_unit_Type200"));
109                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "assault_unit_Type201"));
110                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "standoff_unit_Type300"));
111                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "standoff_unit_Type301"));
112                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "standoff_unit_Type302"));
113                
114                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.OVERSEER, "overseer_unit_Type250"));
115                CONSTRUCTABLE.add(new SwarmConstructableVariant(SwarmConstructableType.HIVE, "hive_unit_Type350"));
116                
117                MIN_CR = 1f;
118                MIN_DP = 100f;
119                MIN_FRAGMENTS = 500;
120                
121                MAX_FRAGMENTS = 0;
122                
123                for (SwarmConstructableVariant v : CONSTRUCTABLE) {
124                        MIN_CR = Math.min(v.cr, MIN_CR);
125                        MIN_DP = Math.min(v.dp, MIN_DP);
126                        MIN_FRAGMENTS = Math.min(v.fragments, MIN_FRAGMENTS);
127                        MAX_FRAGMENTS = Math.max(v.fragments, MAX_FRAGMENTS);
128                }
129        }
130        
131        
132        public static class SwarmConstructionData {
133                public String variantId;
134                public float constructionTime = 10f;
135                public float preConstructionTravelTime = 3f;
136        }
137        
138        
139        protected WeightedRandomPicker<WeaponSlotAPI> slots;
140        protected boolean readyToFire = true;
141        protected int fastConstructionLeft = FAST_CONSTRUCTION_FRIGATES_MAX;
142        
143        protected void findSlots(ShipAPI ship) {
144                if (slots != null) return;
145                slots = new WeightedRandomPicker<>();
146                for (WeaponSlotAPI slot : ship.getHullSpec().getAllWeaponSlotsCopy()) {
147                        if (slot.isSystemSlot()) {
148                                slots.add(slot);
149                        }
150                }
151        }
152        
153        public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
154                ShipAPI ship = null;
155                //boolean player = false;
156                if (stats.getEntity() instanceof ShipAPI) {
157                        ship = (ShipAPI) stats.getEntity();
158                        //player = ship == Global.getCombatEngine().getPlayerShip();
159                } else {
160                        return;
161                }
162                
163                init();
164                
165                if (state == State.IDLE || state == State.COOLDOWN || effectLevel <= 0f) {
166                        readyToFire = true;
167                }
168                
169                if (effectLevel == 1 && readyToFire) {
170                        readyToFire = false;
171                        launchSwarm(ship);
172                }
173        }
174        
175        
176        protected void launchSwarm(ShipAPI ship) {
177                findSlots(ship);
178                
179                String wingId = SwarmLauncherEffect.CONSTRUCTION_SWARM_WING;
180
181                CombatEngineAPI engine = Global.getCombatEngine();
182                CombatFleetManagerAPI manager = engine.getFleetManager(ship.getOwner());
183                manager.setSuppressDeploymentMessages(true);
184                
185                WeaponSlotAPI slot = slots.pick();
186                
187                Vector2f loc = slot.computePosition(ship);
188                float facing = slot.computeMidArcAngle(ship);
189                
190                ShipAPI fighter = manager.spawnShipOrWing(wingId, loc, facing, 0f, null);
191                fighter.getWing().setSourceShip(ship);
192                
193                manager.setSuppressDeploymentMessages(false);
194                
195                fighter.getMutableStats().getMaxSpeed().modifyMult("construction_swarm", CONSTRUCTION_SWARM_SPEED_MULT);
196                
197                Vector2f takeoffVel = Misc.getUnitVectorAtDegreeAngle(facing);
198                takeoffVel.scale(fighter.getMaxSpeed() * 1f);
199                
200                fighter.setDoNotRender(true);
201                fighter.setExplosionScale(0f);
202                fighter.setHulkChanceOverride(0f);
203                fighter.setImpactVolumeMult(SwarmLauncherEffect.IMPACT_VOLUME_MULT);
204                fighter.getArmorGrid().clearComponentMap(); // no damage to weapons/engines
205                Vector2f.add(fighter.getVelocity(), takeoffVel, fighter.getVelocity());
206                
207                RoilingSwarmEffect sourceSwarm = RoilingSwarmEffect.getSwarmFor(ship);
208                if (sourceSwarm == null) return;
209                
210                RoilingSwarmEffect swarm = FragmentSwarmHullmod.createSwarmFor(fighter);
211                swarm.params.flashFringeColor = VoltaicDischargeOnFireEffect.EMP_FRINGE_COLOR;
212                RoilingSwarmEffect.getFlockingMap().remove(swarm.params.flockingClass, swarm);
213                swarm.params.flockingClass = FragmentSwarmHullmod.CONSTRUCTION_SWARM_FLOCKING_CLASS;
214                RoilingSwarmEffect.getFlockingMap().add(swarm.params.flockingClass, swarm);
215
216                
217                SwarmConstructableVariant pick = pickVariant(ship);
218                if (pick == null) return;
219                
220                String variantId = pick.variantId;
221//              variantId = "standoff_unit_Type300";
222//              variantId = "overseer_unit_Type250";
223//              variantId = "skirmish_unit_Type100";
224//              variantId = "assault_unit_Type200";
225//              variantId = "assault_unit_Type201"; // no swarm
226//              variantId = "hive_unit_Type350";
227                
228                ShipVariantAPI variant = Global.getSettings().getVariant(variantId);
229                if (variant == null) return;
230                
231                ship.setCurrentCR(ship.getCurrentCR() - pick.cr);
232                
233                float dp = variant.getHullSpec().getSuppliesToRecover();
234                
235                int numFragments = pick.fragments;
236                float radiusMult = 1f;
237                float collisionMult = 2f;
238                float hpMult = 1f;
239                float travelTime = 3f;
240                
241                if (variant.getHullSize() == HullSize.DESTROYER) {
242                        radiusMult = 2f;
243                        collisionMult = 4f;
244                        hpMult = radiusMult;
245                        travelTime = 4f;
246                } else if (variant.getHullSize() == HullSize.CRUISER) {
247                        radiusMult = 3.5f;
248                        collisionMult = 6f;
249                        hpMult = radiusMult;
250                        travelTime = 5f;
251                } else if (variant.getHullSize() == HullSize.CAPITAL_SHIP) {
252                        radiusMult = 4;
253                        collisionMult = 8f;
254                        hpMult = radiusMult;
255                        travelTime = 6f;
256                }
257                
258                for (SegmentAPI s : fighter.getExactBounds().getOrigSegments()) {
259                        s.getP1().scale(collisionMult);
260                        s.getP2().scale(collisionMult);
261                        s.set(s.getP1().x, s.getP1().y, s.getP2().x, s.getP2().y);
262                }
263                fighter.setCollisionRadius(fighter.getCollisionRadius() * collisionMult);
264                
265                fighter.setMaxHitpoints(fighter.getMaxHitpoints() * hpMult);
266                fighter.setHitpoints(fighter.getHitpoints() * hpMult);
267                
268                swarm.params.maxOffset *= radiusMult;
269//              swarm.params.initialMembers *= numMult;
270//              swarm.params.baseMembersToMaintain = swarm.params.initialMembers;
271//              requiredFragments = swarm.params.initialMembers;
272                swarm.params.initialMembers = numFragments;
273                swarm.params.baseMembersToMaintain = numFragments;
274                
275                boolean overseer = variant.getHullSpec().hasTag(Tags.THREAT_OVERSEER);
276                
277                SwarmConstructionData data = new SwarmConstructionData();
278                data.variantId = variantId;
279                data.constructionTime = BASE_CONSTRUCTION_TIME + dp * CONSTRUCTION_TIME_DP_MULT;
280                if (overseer) {
281                        data.constructionTime += CONSTRUCTION_TIME_OVERSEER_EXTRA;
282                }
283                data.preConstructionTravelTime = travelTime;
284                
285                if (fastConstructionLeft > 0) {
286                        if (pick.size == HullSize.FRIGATE) {
287                                fastConstructionLeft--;
288                                data.constructionTime = 2f;
289                        } else {
290                                fastConstructionLeft = 0;
291                        }
292                }
293                
294                swarm.custom1 = data;
295                
296                int transfer = Math.min(numFragments, sourceSwarm.getNumActiveMembers());
297                if (transfer > 0) {
298                        loc = new Vector2f(takeoffVel);
299                        loc.scale(0.5f);
300                        Vector2f.add(loc, fighter.getLocation(), loc);
301                        sourceSwarm.transferMembersTo(swarm, transfer, loc, 100f);
302                }
303                
304                int add = numFragments - transfer;
305                if (add > 0) {
306                        swarm.addMembers(add);
307                }
308        }
309        
310        
311        public SwarmConstructableVariant pickVariant(ShipAPI ship) {
312                init();
313                
314//              if (true) {
315//                      return new SwarmConstructableVariant(SwarmConstructableType.COMBAT_UNIT, "standoff_unit_Type302");
316//              }
317                
318                CombatEngineAPI engine = Global.getCombatEngine();
319                CombatFleetManagerAPI manager = engine.getFleetManager(ship.getOwner());
320                if (manager == null) return null;
321                
322                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
323                int fragments = swarm == null ? 0 : swarm.getNumActiveMembers();
324                
325                int dpLeft = manager.getMaxStrength() - manager.getCurrStrength();
326                float cr = ship.getCurrentCR();
327                
328                int overseers = getNumOverseersDeployed(manager);
329                int hives = getNumHivesDeployed(manager);
330                int fabricators = getNumFabricatorsDeployed(manager);
331                float combatWeight = getCombatWeightDeployed(manager);
332                
333                
334                int wantOverseers = (int) (combatWeight / 8f);
335                if (wantOverseers < 1) wantOverseers = 1;
336                
337                combatWeight += Math.max(0, fabricators - 1f) * 16f;
338                int wantHives = (int) (combatWeight / 16f);
339                
340                if (wantHives < 1) wantHives = 1; 
341                if (wantHives > 2) wantHives = 2;
342                
343                wantOverseers -= overseers;
344                wantHives -= hives;
345                
346                float frigates = getCombatDeployed(manager, HullSize.FRIGATE);
347                float destroyers = getCombatDeployed(manager, HullSize.DESTROYER);
348                float cruisers = getCombatDeployed(manager, HullSize.CRUISER);
349                float capitals = getCombatDeployed(manager, HullSize.CAPITAL_SHIP);
350                float large = cruisers + capitals;
351                
352                if (frigates >= 2) {
353                        fastConstructionLeft = 0;
354                }
355                
356                CountingMap<HullSize> numCombatVariants = new CountingMap<>();
357                for (SwarmConstructableVariant curr : CONSTRUCTABLE) {
358                        if (curr.type == SwarmConstructableType.COMBAT_UNIT) {
359                                numCombatVariants.add(curr.size);
360                        }
361                }
362                
363                WeightedRandomPicker<SwarmConstructableVariant> hivePicker = new WeightedRandomPicker<>();
364                WeightedRandomPicker<SwarmConstructableVariant> overseerPicker = new WeightedRandomPicker<>();
365                WeightedRandomPicker<SwarmConstructableVariant> smallPicker = new WeightedRandomPicker<>();
366                WeightedRandomPicker<SwarmConstructableVariant> mediumPicker = new WeightedRandomPicker<>();
367                WeightedRandomPicker<SwarmConstructableVariant> largePicker = new WeightedRandomPicker<>();
368                
369                for (SwarmConstructableVariant curr : CONSTRUCTABLE) {
370                        if (curr.dp > dpLeft) continue;
371                        if (curr.cr > cr) continue;
372                        if (curr.fragments > fragments) continue;
373                        
374                        if (curr.type == SwarmConstructableType.HIVE) {
375                                hivePicker.add(curr, 1f / curr.dp);
376                        } else if (curr.type == SwarmConstructableType.OVERSEER) {
377                                overseerPicker.add(curr, 1f / curr.dp);
378                        } else {
379                                float wMult = 1f / Math.max(1f, numCombatVariants.getCount(curr.size));
380                                if (curr.size == HullSize.FRIGATE) {
381                                        smallPicker.add(curr, 1f / curr.dp * wMult);
382                                } else if (curr.size == HullSize.DESTROYER) {
383                                        mediumPicker.add(curr, 1f / curr.dp * wMult);
384                                } else {
385                                        largePicker.add(curr, 1f / curr.dp * wMult);
386                                }
387                        }
388                }
389                
390                if (frigates <= 1 && !smallPicker.isEmpty()) {
391                        return smallPicker.pick();
392                }
393                
394                if (wantOverseers > 0 || wantHives > 0) {
395                        if (wantOverseers >= wantHives && !overseerPicker.isEmpty()) {
396                                return overseerPicker.pick();
397                        } else if (!hivePicker.isEmpty()) {
398                                return hivePicker.pick();
399                        }
400                }
401                
402                if (large <= destroyers * NUM_LARGE_AS_FRACTION_OF_DESTROYERS && !largePicker.isEmpty()) {
403                        return largePicker.pick();
404                }
405                
406                if (destroyers <= frigates * NUM_DESTROYERS_AS_FRACTION_OF_FRIGATES && !mediumPicker.isEmpty()) {
407                        return mediumPicker.pick();
408                }
409                
410                return smallPicker.pick();
411        }
412        
413        public static boolean constructionSwarmWillBuild(ShipAPI ship, String tag, HullSize size) {
414                if (!ship.isFighter() || ship.hasTag(ThreatShipConstructionScript.SWARM_CONSTRUCTING_SHIP)) {
415                        return false;
416                }
417                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
418                if (swarm == null) {
419                        return false;
420                }
421                
422                if (swarm.custom1 instanceof SwarmConstructionData) {
423                        SwarmConstructionData data = (SwarmConstructionData) swarm.custom1;
424                        ShipVariantAPI v = Global.getSettings().getVariant(data.variantId);
425                        if (v.getHullSpec().hasTag(tag)) {
426                                return size == null || v.getHullSize() == size;
427                        }
428                }
429                return false;
430        }
431        
432        public static int getNumFabricatorsDeployed(CombatFleetManagerAPI manager) {
433                init();
434                int count = 0;
435                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
436                        ShipAPI ship = dfm.getShip();
437                        if (ship == null) continue;
438                        
439                        if (ship.getHullSpec().hasTag(Tags.THREAT_FABRICATOR)) {
440                                count++;
441                        }
442                }
443                return count;
444        }
445        
446        public static int getNumOverseersDeployed(CombatFleetManagerAPI manager) {
447                init();
448                int count = 0;
449                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
450                        ShipAPI ship = dfm.getShip();
451                        if (ship == null) continue;
452                        
453                        if (constructionSwarmWillBuild(ship, Tags.THREAT_OVERSEER, null)) {
454                                count++;
455                                continue;
456                        }
457                        if (ship.isFighter()) continue;
458                        
459                        
460                        if (ship.getHullSpec().hasTag(Tags.THREAT_OVERSEER)) {
461                                count++;
462                        }
463                }
464                return count;
465        }
466        
467        public static int getNumHivesDeployed(CombatFleetManagerAPI manager) {
468                init();
469                int count = 0;
470                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
471                        ShipAPI ship = dfm.getShip();
472                        if (ship == null) continue;
473                        
474                        if (constructionSwarmWillBuild(ship, Tags.THREAT_HIVE, null)) {
475                                count++;
476                                continue;
477                        }
478                        if (ship.isFighter()) continue;
479                        
480                        if (ship.getHullSpec().hasTag(Tags.THREAT_HIVE)) {
481                                count++;
482                        }
483                }
484                return count;
485        }
486        
487        public static float getCombatWeightDeployed(CombatFleetManagerAPI manager) {
488                init();
489                float weight = 0;
490                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
491                        ShipAPI ship = dfm.getShip();
492                        if (ship == null) continue;
493                        
494                        if (ship.isFighter() && !ship.hasTag(ThreatShipConstructionScript.SWARM_CONSTRUCTING_SHIP)) {
495                                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
496                                if (swarm != null && swarm.custom1 instanceof SwarmConstructionData) {
497                                        SwarmConstructionData data = (SwarmConstructionData) swarm.custom1;
498                                        ShipVariantAPI v = Global.getSettings().getVariant(data.variantId);
499                                        if (v.getHullSpec().hasTag(Tags.THREAT_COMBAT)) {
500                                                switch (v.getHullSize()) {
501                                                case CAPITAL_SHIP: weight += 8; break;
502                                                case CRUISER: weight += 4; break;
503                                                case DESTROYER: weight += 2; break;
504                                                case FRIGATE: weight += 1; break;
505                                                case FIGHTER: weight += 1; break;
506                                                }
507                                        }
508                                }
509                                continue;
510                        }
511                        
512                        if (ship.getHullSpec().hasTag(Tags.THREAT_COMBAT)) {
513                                weight += Misc.getShipWeight(ship, false);
514                        }
515                }
516                return weight;
517        }
518        
519        public static int getCombatDeployed(CombatFleetManagerAPI manager, HullSize size) {
520                init();
521                int count = 0;
522                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
523                        ShipAPI ship = dfm.getShip();
524                        if (ship == null) continue;
525                        
526                        if (constructionSwarmWillBuild(ship, Tags.THREAT_COMBAT, size)) {
527                                count++;
528                                continue;
529                        }
530                        if (ship.isFighter() || ship.getHullSize() != size) continue;
531                        
532                        if (ship.getHullSpec().hasTag(Tags.THREAT_COMBAT)) {
533                                count++;
534                        }
535                }
536                return count;
537        }
538        
539        
540        public static int getFragmentCost(float dp, HullSize size) {
541                float numMult = 1f * dp / 5f;
542                if (size == HullSize.DESTROYER) {
543                        numMult = 2f * dp / 20f;
544                } else if (size == HullSize.CRUISER) {
545                        numMult = 3f * dp / 20f;
546                } else if (size == HullSize.CAPITAL_SHIP) {
547                        numMult = 5f * dp / 40f;
548                }
549                return (int) Math.round(BASE_FRAGMENTS * numMult);
550        }
551
552        
553        @Override
554        public String getInfoText(ShipSystemAPI system, ShipAPI ship) {
555                init();
556                if (system.isOutOfAmmo()) return null;
557                if (system.getState() != SystemState.IDLE) return null;
558                
559                if (!enoughFragments(system, ship)) {
560                        return "LOW FRAGMENTS";
561                }
562                if (!enoughDP(system, ship)) {
563                        return "LOW DP";
564                }
565                if (!enoughCR(system, ship)) {
566                        return "LOW CR";
567                }
568                return "READY";
569        }
570        
571        public boolean enoughCR(ShipSystemAPI system, ShipAPI ship) {
572                return ship.getCurrentCR() >= MIN_CR;
573        }
574        public boolean enoughDP(ShipSystemAPI system, ShipAPI ship) {
575                CombatEngineAPI engine = Global.getCombatEngine();
576                CombatFleetManagerAPI manager = engine.getFleetManager(ship.getOwner());
577                if (manager == null) return true;
578                
579                int dpLeft = manager.getMaxStrength() - manager.getCurrStrength();
580                
581                for (DeployedFleetMemberAPI dfm : manager.getDeployedCopyDFM()) {
582                        ShipAPI ship2 = dfm.getShip();
583                        if (ship2 == null) continue;
584                        
585                        if (!ship2.isFighter() || ship2.hasTag(ThreatShipConstructionScript.SWARM_CONSTRUCTING_SHIP)) {
586                                continue;
587                        }
588                        RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship2);
589                        if (swarm == null) {
590                                continue;
591                        }
592                        if (swarm.custom1 instanceof SwarmConstructionData) {
593                                SwarmConstructionData data = (SwarmConstructionData) swarm.custom1;
594                                ShipVariantAPI v = Global.getSettings().getVariant(data.variantId);
595                                dpLeft -= v.getHullSpec().getSuppliesToRecover();
596                        }
597                }
598                
599                return dpLeft >= MIN_DP;
600        }
601        public boolean enoughFragments(ShipSystemAPI system, ShipAPI ship) {
602                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
603                int active = swarm == null ? 0 : swarm.getNumActiveMembers();
604                int required = MIN_FRAGMENTS;
605                return active >= required;
606        }
607
608        @Override
609        public boolean isUsable(ShipSystemAPI system, ShipAPI ship) {
610                init();
611                return enoughFragments(system, ship) && enoughDP(system, ship) && enoughCR(system, ship);
612        }
613        
614}
615
616
617
618
619
620
621
622