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