001package com.fs.starfarer.api.impl.campaign.abilities; 002 003import java.util.LinkedHashMap; 004import java.util.Map; 005import java.util.Random; 006 007import java.awt.Color; 008 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.EveryFrameScript; 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.CampaignFleetAPI; 014import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 015import com.fs.starfarer.api.campaign.FactionAPI; 016import com.fs.starfarer.api.campaign.JumpPointAPI; 017import com.fs.starfarer.api.campaign.PlanetAPI; 018import com.fs.starfarer.api.campaign.PlanetSpecAPI; 019import com.fs.starfarer.api.campaign.SectorEntityToken; 020import com.fs.starfarer.api.characters.AbilityPlugin; 021import com.fs.starfarer.api.fleet.FleetMemberViewAPI; 022import com.fs.starfarer.api.impl.campaign.ids.Abilities; 023import com.fs.starfarer.api.impl.campaign.ids.Factions; 024import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 025import com.fs.starfarer.api.impl.campaign.ids.Pings; 026import com.fs.starfarer.api.impl.campaign.ids.StarTypes; 027import com.fs.starfarer.api.impl.campaign.ids.Tags; 028import com.fs.starfarer.api.impl.campaign.ids.Terrain; 029import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin; 030import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2; 031import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2; 032import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment; 033import com.fs.starfarer.api.ui.Alignment; 034import com.fs.starfarer.api.ui.TooltipMakerAPI; 035import com.fs.starfarer.api.util.Misc; 036import com.fs.starfarer.api.util.Misc.FleetMemberDamageLevel; 037 038public class GenerateSlipsurgeAbility extends DurationAbilityWithCost2 { 039 040 public static boolean REQUIRE_GIANT_STARS_OR_STRONGER = true; 041 042 public static Map<String, Float> SLIPSURGE_STRENGTH = new LinkedHashMap<String, Float>(); 043 static { 044 // I'm aware that these mappings do not reflect actual star mass, which varies 045 // wildly in any case please don't @ me thank you -am 046 047 SLIPSURGE_STRENGTH.put(StarTypes.BLACK_HOLE, 1f); 048 SLIPSURGE_STRENGTH.put(StarTypes.NEUTRON_STAR, 0.9f); 049 050 SLIPSURGE_STRENGTH.put(StarTypes.BLUE_SUPERGIANT, 0.8f); 051 SLIPSURGE_STRENGTH.put(StarTypes.RED_SUPERGIANT, 0.8f); 052 053 SLIPSURGE_STRENGTH.put(StarTypes.RED_GIANT, 0.6f); 054 SLIPSURGE_STRENGTH.put(StarTypes.BLUE_GIANT, 0.6f); 055 SLIPSURGE_STRENGTH.put(StarTypes.ORANGE_GIANT, 0.6f); 056 057 SLIPSURGE_STRENGTH.put(StarTypes.ORANGE, 0.4f); 058 SLIPSURGE_STRENGTH.put(StarTypes.YELLOW, 0.4f); 059 060 SLIPSURGE_STRENGTH.put(StarTypes.WHITE_DWARF, 0.25f); 061 SLIPSURGE_STRENGTH.put(StarTypes.RED_DWARF, 0.2f); 062 063 SLIPSURGE_STRENGTH.put(StarTypes.BROWN_DWARF, 0.1f); 064 065 SLIPSURGE_STRENGTH.put(StarTypes.GAS_GIANT, 0f); 066 SLIPSURGE_STRENGTH.put(StarTypes.ICE_GIANT, 0f); 067 } 068 069 public static float SLIPSURGE_STRENGTH_MULT = 1.3f; 070 071 public static float TRANSIT_MUSIC_SUPPRESSION = 1f; 072 public static String TRANSIT_SOUND_LOOP = "ui_slipsurge_travel_loop"; 073 074 public static float FUEL_COST_MULT = 5f; 075 public static float CR_COST_MULT = 0.25f; 076 public static float SENSOR_RANGE_MULT = 0.1f; 077 078 public static String SENSOR_MOD_ID = "slipsurge_sensor_penalty"; 079 080 public static class SlipsurgeFadeInScript implements EveryFrameScript { 081 protected SlipstreamTerrainPlugin2 plugin; 082 protected boolean done = false; 083 protected int index = 0; 084 protected float elapsed = 0f; 085 protected float maxElapsed = 0f; 086 public SlipsurgeFadeInScript(SlipstreamTerrainPlugin2 plugin) { 087 this.plugin = plugin; 088 089 float fadeInLength = 300f; 090 float lengthSoFar = 0f; 091 float durIn = 0.2f; 092 maxElapsed = durIn * plugin.getSegments().size() * 0.34f + 3f; 093 for (SlipstreamSegment seg : plugin.getSegments()) { 094 seg.fader.setDurationIn(durIn); 095 seg.fader.forceOut(); 096 097 seg.bMult = Math.min(1f, lengthSoFar / fadeInLength); 098 lengthSoFar += seg.lengthToNext; 099 } 100 } 101 public void advance(float amount) { 102 if (done) return; 103 104 // amount == 1 when not current location 105 // which isn't fine-grained enough to fade the segments in 106 // one at a time since each takes <1s 107 if (!plugin.getEntity().isInCurrentLocation()) { 108 for (SlipstreamSegment curr : plugin.getSegments()) { 109 curr.fader.fadeIn(); 110 } 111 done = true; 112 return; 113 } 114 115 116 elapsed += amount; 117 if (index >= plugin.getSegments().size() || elapsed > maxElapsed) { 118 done = true; 119 return; 120 } 121 SlipstreamSegment curr = plugin.getSegments().get(index); 122 if (curr.fader.isFadedIn()) { 123 index++; 124 return; 125 } 126 curr.fader.fadeIn(); 127 if (curr.fader.getBrightness() > 0.33f && index < plugin.getSegments().size() - 1) { 128 plugin.getSegments().get(index + 1).fader.fadeIn(); 129 } 130 if (curr.fader.getBrightness() > 0.67f && index < plugin.getSegments().size() - 2) { 131 plugin.getSegments().get(index + 2).fader.fadeIn(); 132 } 133 } 134 135 public boolean isDone() { 136 return done; 137 } 138 public boolean runWhilePaused() { 139 return false; 140 } 141 } 142 143 public static class ExpandStormRadiusScript implements EveryFrameScript { 144 protected float elapsed = 0f; 145 protected float extraRadius; 146 public ExpandStormRadiusScript(float extraRadius) { 147 this.extraRadius = extraRadius; 148 } 149 public void advance(float amount) { 150 elapsed += amount; 151 152 CampaignTerrainAPI terrain = Misc.getHyperspaceTerrain(); 153 if (terrain != null) { 154 HyperspaceTerrainPlugin htp = (HyperspaceTerrainPlugin) terrain.getPlugin(); 155 htp.setExtraDistanceAroundPlayerToAdvanceStormCells(extraRadius); 156 htp.setStormCellTimeMultOutsideBaseArea(5f); 157 } 158 } 159 160 public boolean isDone() { 161 return elapsed >= 10f; 162 } 163 public boolean runWhilePaused() { 164 return false; 165 } 166 } 167 168 public static class SlipsurgeEffectScript implements EveryFrameScript { 169 protected CampaignFleetAPI fleet; 170 protected boolean triggered = false; 171 protected boolean done = false; 172 protected boolean didTempCleanup = false; 173 protected float elapsed = 0f; 174 protected SlipstreamTerrainPlugin2 plugin; 175 public SlipsurgeEffectScript(CampaignFleetAPI fleet, SlipstreamTerrainPlugin2 plugin) { 176 this.fleet = fleet; 177 this.plugin = plugin; 178 } 179 180 public void abort() { 181 done = true; 182 didTempCleanup = true; 183 fleet.fadeInIndicator(); 184 fleet.setNoEngaging(0f); 185 fleet.getStats().getSensorRangeMod().unmodifyMult(SENSOR_MOD_ID); 186 for (FleetMemberViewAPI view : fleet.getViews()) { 187 view.endJitter(); 188 } 189 if (fleet.isPlayerFleet()) { 190 Global.getSector().getCampaignUI().setFollowingDirectCommand(false); 191 } 192 //fleet.setMoveDestination(dest.x, dest.y) 193 } 194 195 public void advance(float amount) { 196 if (done) return; 197 198 if (fleet.isInHyperspaceTransition() || 199 plugin.getEntity().getContainingLocation() != fleet.getContainingLocation()) { 200 if (!didTempCleanup) { 201 abort(); 202 } 203 // fleet could conceivably come back, set done back to false so the script keeps running 204 done = false; 205 return; 206 } 207 208 elapsed += amount; 209 if (!triggered) { 210 if (elapsed >= 30f || 211 !plugin.getEntity().isAlive()) { 212 done = true; 213 return; 214 } 215 } 216 217 float burn = Misc.getBurnLevelForSpeed(fleet.getVelocity().length()); 218 if (burn > 50 && plugin.containsEntity(fleet)) { 219 triggered = true; 220 } 221 222 if (triggered) { 223 if (burn < 50) { 224 plugin.despawn(0, 0.2f, new Random()); 225 abort(); 226 return; 227 } 228 229 AbilityPlugin gs = fleet.getAbility(Abilities.GENERATE_SLIPSURGE); 230 if (gs instanceof BaseAbilityPlugin) { 231 BaseAbilityPlugin base = (BaseAbilityPlugin) gs; 232 for (AbilityPlugin curr : fleet.getAbilities().values()) { 233 if (curr == this) continue; 234 if (!base.isCompatible(curr)) { 235 if (curr.isActiveOrInProgress()) { 236 curr.deactivate(); 237 } 238 curr.forceDisable(); 239 } 240 } 241 } 242 243 float decelMult = Misc.getAbyssalDepth(fleet) * 1f; 244 if (fleet.getGoSlowOneFrame()) decelMult = 1f; 245 //if (fleet.getGoSlowOneFrame() && !plugin.containsEntity(fleet)) { 246 if (decelMult > 0f && !plugin.containsEntity(fleet)) { 247 float angle = Misc.getAngleInDegrees(fleet.getVelocity()); 248 Vector2f decelDir = Misc.getUnitVectorAtDegreeAngle(angle + 180f); 249 250 float targetDecelSeconds = 0.5f; 251 float speed = fleet.getVelocity().length(); 252 float decelAmount = amount * speed / targetDecelSeconds; 253 decelDir.scale(decelAmount * decelMult); 254 255 Vector2f vel = fleet.getVelocity(); 256 fleet.setVelocity(vel.x + decelDir.x, vel.y + decelDir.y); 257 } 258 259 260 fleet.fadeOutIndicator(); 261 fleet.setNoEngaging(0.1f); 262 fleet.setInteractionTarget(null); 263 if (fleet.isPlayerFleet()) { 264 Global.getSector().getCampaignUI().setFollowingDirectCommand(true); 265 } 266 fleet.getMemoryWithoutUpdate().set(MemFlags.NO_HIGH_BURN_TOPOGRAPHY_READINGS, true, 0.1f); 267 fleet.getStats().getSensorRangeMod().modifyMult(SENSOR_MOD_ID, SENSOR_RANGE_MULT, "Extreme burn level"); 268 didTempCleanup = false; 269 270 float angle = Misc.getAngleInDegrees(fleet.getVelocity()); 271 Vector2f jitterDir = Misc.getUnitVectorAtDegreeAngle(angle + 180f); 272 Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(angle); 273 float windIntensity = (burn - 50f) / 250f; 274 if (windIntensity < 0) windIntensity = 0; 275 //if (windIntensity > 1f) windIntensity = 1f; 276 277 float b = (burn - 50f) / 450f; 278 if (b < 0) b = 0; 279 if (b > 1f) b = 1f; 280 if (b > 0) { 281 282 if (fleet.isPlayerFleet()) { 283 float volume = b; 284 Global.getSector().getCampaignUI().suppressMusic(TRANSIT_MUSIC_SUPPRESSION * volume); 285 Global.getSoundPlayer().setNextLoopFadeInAndOut(0.05f, 0.5f); 286 Global.getSoundPlayer().playLoop(TRANSIT_SOUND_LOOP, fleet, 287 1f, volume, 288 fleet.getLocation(), fleet.getVelocity()); 289 } 290 291 292 //String modId = "SlipsurgeEffectScript_" + fleet.getId(); 293 for (FleetMemberViewAPI view : fleet.getViews()) { 294 Color c = view.getMember().getHullSpec().getHyperspaceJitterColor(); 295// view.setJitter(1f, 1f, c, 7, 10f); 296// view.setJitterBrightness(b); 297// //view.setJitter(0.25f, 1f, c, 10 + Math.round(40f * b), 20f); 298// view.setUseCircularJitter(true); 299 300 c = Misc.setAlpha(c, 60); 301 view.setJitter(0.1f, 1f, c, 10 + Math.round(40f * b), 20f); 302 view.setUseCircularJitter(true); 303 view.setJitterDirection(jitterDir); 304 view.setJitterLength(30f * b); 305 view.setJitterBrightness(b); 306 307 } 308 } else { 309 for (FleetMemberViewAPI view : fleet.getViews()) { 310 view.endJitter(); 311 } 312 } 313 } 314 315 } 316 317 public boolean isDone() { 318 return done; 319 } 320 public boolean runWhilePaused() { 321 return false; 322 } 323 } 324 325 protected Boolean primed = null; 326 protected Vector2f startLoc = null; 327 protected JumpPointAPI well = null; 328 329 330 public float getFuelCostMult() { 331 return FUEL_COST_MULT; 332 } 333 public float getCRCostMult() { 334 return CR_COST_MULT; 335 } 336 public FleetMemberDamageLevel getActivationDamageLevel() { 337 return FleetMemberDamageLevel.MEDIUM; 338 } 339 public boolean canRecoverCRWhileActive() { 340 return true; 341 } 342 343 344 @Override 345 protected void activateImpl() { 346 CampaignFleetAPI fleet = getFleet(); 347 if (fleet == null) return; 348 349 primed = true; 350 well = findGravityWell(); 351 if (well == null) return; 352 353 //startLoc = new Vector2f(fleet.getLocation()); 354 float angle = Misc.getAngleInDegrees(well.getLocation(), fleet.getLocation()); 355 float offset = 100f; 356 Vector2f from = Misc.getUnitVectorAtDegreeAngle(angle); 357 from.scale(offset + fleet.getRadius()); 358 Vector2f.add(from, fleet.getLocation(), from); 359 startLoc = from; 360 361 362 SectorEntityToken token = fleet.getContainingLocation().createToken(startLoc); 363 //Global.getSector().addPing(fleet, Pings.SLIPSURGE); 364 Global.getSector().addPing(token, Pings.SLIPSURGE); 365 366 well.getContainingLocation().addScript(new ExpandStormRadiusScript(16000f)); 367 368 deductCost(); 369 } 370 371 @Override 372 protected void applyStatsEffect(float amount, float level) { 373 CampaignFleetAPI fleet = getFleet(); 374 if (fleet == null) return; 375 376 if (level > 0 && level < 1 && amount > 0) { 377 fleet.goSlowOneFrame(); 378 return; 379 } 380 381 if (level == 1 && primed != null) { 382 generateSlipstream(); 383 primed = null; 384 startLoc = null; 385 well = null; 386 } 387 388 } 389 390 391 protected void generateSlipstream() { 392 CampaignFleetAPI fleet = getFleet(); 393 if (fleet == null) return; 394 395 //JumpPointAPI jp = findGravityWell(); 396 JumpPointAPI jp = well; 397 if (jp == null) return; 398 399 float strength = getStrengthForGravityWell(jp); 400 strength *= SLIPSURGE_STRENGTH_MULT; 401 402 float angle = Misc.getAngleInDegrees(jp.getLocation(), startLoc); 403 float offset = 100f; 404// Vector2f from = Misc.getUnitVectorAtDegreeAngle(angle); 405// from.scale(offset + fleet.getRadius()); 406// Vector2f.add(from, startLoc, from); 407 Vector2f from = startLoc; 408 409 SlipstreamParams2 params = new SlipstreamParams2(); 410 411 params.enteringSlipstreamTextOverride = "Entering slipsurge"; 412 params.enteringSlipstreamTextDurationOverride = 0.1f; 413 params.forceNoWindVisualEffectOnFleets = true; 414 415 float width = 600f; 416 float length = 1000f; 417 418 length += strength * 500f; 419 params.burnLevel = Math.round(400f + strength * strength * 500f); 420 params.accelerationMult = 20f + strength * strength * 280f; 421 422 //params.accelerationMult = 500f; 423 424 params.baseWidth = width; 425 params.widthForMaxSpeed = 400f; 426 params.widthForMaxSpeedMinMult = 0.34f; 427 //params.widthForMaxSpeed = 300f; 428 params.slowDownInWiderSections = true; 429 //params.edgeWidth = 100f; 430 //params.accelerationMult = 100f; 431 432 433 params.minSpeed = Misc.getSpeedForBurnLevel(params.burnLevel - params.burnLevel/8); 434 params.maxSpeed = Misc.getSpeedForBurnLevel(params.burnLevel + params.burnLevel/8); 435 //params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (float) params.burnLevel)); 436 params.lineLengthFractionOfSpeed = 2000f / ((params.maxSpeed + params.minSpeed) * 0.5f); 437 438 float lineFactor = 0.1f; 439 params.minSpeed *= lineFactor; 440 params.maxSpeed *= lineFactor; 441 //params.lineLengthFractionOfSpeed *= 0.25f; 442 //params.lineLengthFractionOfSpeed *= 1f; 443 params.maxBurnLevelForTextureScroll = (int) (params.burnLevel * 0.1f); 444 445 params.particleFadeInTime = 0.01f; 446 params.areaPerParticle = 1000f; 447 448 Vector2f to = Misc.getUnitVectorAtDegreeAngle(angle); 449 to.scale(offset + fleet.getRadius() + length); 450 Vector2f.add(to, startLoc, to); 451 452 CampaignTerrainAPI slipstream = (CampaignTerrainAPI) well.getContainingLocation().addTerrain(Terrain.SLIPSTREAM, params); 453 slipstream.addTag(Tags.SLIPSTREAM_VISIBLE_IN_ABYSS); 454 slipstream.setLocation(from.x, from.y); 455 456 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) slipstream.getPlugin(); 457 458 float spacing = 100f; 459 float incr = spacing / length; 460 461 Vector2f diff = Vector2f.sub(to, from, new Vector2f()); 462 for (float f = 0; f <= 1f; f += incr) { 463 Vector2f curr = new Vector2f(diff); 464 curr.scale(f); 465 Vector2f.add(curr, from, curr); 466 plugin.addSegment(curr, width - Math.min(300f, 300f * (float)Math.sqrt(f))); 467 //plugin.addSegment(curr, width); 468 } 469 470 plugin.recomputeIfNeeded(); 471 472 plugin.despawn(1.5f, 0.2f, new Random()); 473 474 slipstream.addScript(new SlipsurgeFadeInScript(plugin)); 475 fleet.addScript(new SlipsurgeEffectScript(fleet, plugin)); 476 477 } 478 479 @Override 480 protected void unapplyStatsEffect() { 481 CampaignFleetAPI fleet = getFleet(); 482 if (fleet == null) return; 483 484 primed = null; 485 startLoc = null; 486 well = null; 487 } 488 489 490 @Override 491 public boolean isUsable() { 492 if (!super.isUsable()) return false; 493 return super.isUsable() && 494 getFleet() != null && 495 getFleet().isInHyperspace() && 496 findGravityWell() != null; 497 //findGravityWell() != null || Misc.isInsideSlipstream(getFleet())); 498 //(getFleet().isAIMode() || computeFuelCost(Misc.isInsideSlipstream(getFleet())) <= getFleet().getCargo().getFuel()); 499 } 500 501 502 @Override 503 public void addInitialDescription(TooltipMakerAPI tooltip, boolean expanded) { 504 CampaignFleetAPI fleet = getFleet(); 505 if (fleet == null) return; 506 507 Color text = Misc.getTextColor(); 508 Color gray = Misc.getGrayColor(); 509 Color highlight = Misc.getHighlightColor(); 510 Color bad = Misc.getNegativeHighlightColor(); 511 512 float pad = 10f; 513 514 515 String extra = ""; 516 if (REQUIRE_GIANT_STARS_OR_STRONGER) { 517 extra = "and availability "; 518 } 519 520 521 // some of the other factors: it has "dwarf" or "giant" in the name 522 tooltip.addPara("Overload and modulate the fleet's drive field " 523 + "to induce an extremely powerful, " 524 + "short-duration slipstream flowing away from the nearest gravity well, its strength " + extra 525 + "based on the stellar object's mass, density, and some other poorly-understood factors. " 526 + "Largely ineffective inside abyssal hyperspace.", pad); 527 528// tooltip.addPara("A stronger surge can allow the fleet to rapidly travel up to %s light-years. " 529// + "Attempting to move slowly " 530// + "during the transit will decelerate the fleet quickly.", pad, highlight, 531// "10", "move slowly"); 532 533 FactionAPI player = Global.getSector().getFaction(Factions.PLAYER); 534 Color starColor = Misc.getBasePlayerColor(); 535 float tw = getTooltipWidth(); 536 float strW = 150f; 537 if (Global.CODEX_TOOLTIP_MODE) { 538 tw = 500f; 539 strW = 220f; 540 } 541 tooltip.beginTable(player, 20f, "Stellar object type", tw - strW, "Surge strength", strW); 542 if (REQUIRE_GIANT_STARS_OR_STRONGER) { 543 tooltip.addRow(Alignment.LMID, starColor, "Black holes, neutron stars", 544 Alignment.MID, highlight, "Extreme"); 545 tooltip.addRow(Alignment.LMID, starColor, "Supergiant stars", 546 Alignment.MID, highlight, "High"); 547 tooltip.addRow(Alignment.LMID, starColor, "Giant stars", 548 Alignment.MID, highlight, "Average"); 549 tooltip.addRow(Alignment.LMID, starColor, "Smaller stars / stellar objects", 550 Alignment.MID, highlight, "---"); 551 } else { 552 tooltip.addRow(Alignment.LMID, starColor, "Black holes, neutron stars", 553 Alignment.MID, highlight, "Extreme"); 554 tooltip.addRow(Alignment.LMID, starColor, "Supergiant stars", 555 Alignment.MID, highlight, "Very high"); 556 tooltip.addRow(Alignment.LMID, starColor, "Giant stars", 557 Alignment.MID, highlight, "High"); 558 tooltip.addRow(Alignment.LMID, starColor, "Sol-like stars", 559 Alignment.MID, highlight, "Average"); 560 tooltip.addRow(Alignment.LMID, starColor, "Dwarf stars", 561 Alignment.MID, highlight, "Low"); 562 tooltip.addRow(Alignment.LMID, starColor, "Gas giants", 563 Alignment.MID, highlight, "Very low"); 564 } 565 566 tooltip.addTable("", 0, pad); 567 568 if (Global.CODEX_TOOLTIP_MODE) { 569 float tw2 = tooltip.getWidthSoFar(); 570 float xOff = (int)((tw2 - tw) / 2f); 571 tooltip.getPrev().getPosition().setXAlignOffset(xOff); 572 tooltip.addSpacer(0f).getPosition().setXAlignOffset(-xOff); 573 } 574 575 tooltip.addSpacer(5f); 576 577 tooltip.addPara("A stronger surge can allow the fleet to rapidly travel up to %s light-years. " 578 + "Attempting to %s " 579 + "during the transit will decelerate the fleet quickly. Fleet sensor range is " 580 + "reduced by %s during the transit.", pad, highlight, 581 "15", "move slowly", 582 "" + (int)Math.round((1f - SENSOR_RANGE_MULT) * 100f) + "%"); 583 584 } 585 586 @Override 587 public boolean addNotUsableReasonBeforeFuelCost(TooltipMakerAPI tooltip, boolean expanded) { 588 CampaignFleetAPI fleet = getFleet(); 589 if (fleet == null) return false; 590 591 Color bad = Misc.getNegativeHighlightColor(); 592 593 float pad = 10f; 594 595 if (!fleet.isInHyperspace()) { 596 tooltip.addPara("Can only be used in hyperspace.", bad, pad); 597 return true; 598 //} else if (findGravityWell() == null && !Misc.isInsideSlipstream(getFleet())) { 599 } else if (findGravityWell() == null) { 600 //tooltip.addPara("Must be near a gravity well or inside a slipstream.", bad, pad); 601 if (REQUIRE_GIANT_STARS_OR_STRONGER) { 602 tooltip.addPara("Must be near a powerful gravity well.", bad, pad); 603 } else { 604 tooltip.addPara("Must be near a gravity well.", bad, pad); 605 } 606 return true; 607 } 608 609 return false; 610 } 611 612 613 public JumpPointAPI findGravityWell() { 614 CampaignFleetAPI fleet = getFleet(); 615 if (fleet == null) return null; 616 617 JumpPointAPI closest = null; 618 float minDist = Float.MAX_VALUE; 619 for (SectorEntityToken curr : fleet.getContainingLocation().getJumpPoints()) { 620 JumpPointAPI jp = (JumpPointAPI) curr; 621 if (!jp.isStarAnchor() && !jp.isGasGiantAnchor()) continue; 622 if (jp.getDestinationVisualEntity() == null) continue; 623 624 if (REQUIRE_GIANT_STARS_OR_STRONGER) { 625 float str = getStrengthForGravityWell(jp); 626 float min = SLIPSURGE_STRENGTH.get(StarTypes.YELLOW); 627 if (str <= min) continue; 628 } 629 630 float dist = Misc.getDistance(fleet, jp) - jp.getRadius(); 631 if (dist > jp.getRadius() + 150f) continue; 632 if (dist < minDist) { 633 closest = jp; 634 minDist = dist; 635 } 636 } 637 return closest; 638 } 639 640 public float getStrengthForGravityWell(JumpPointAPI jp) { 641 return getStrengthForStellarObject(jp.getDestinationVisualEntity()); 642 } 643 644 public float getStrengthForStellarObject(SectorEntityToken object) { 645 if (!(object instanceof PlanetAPI)) return 0f; 646 PlanetAPI star = (PlanetAPI) object; 647 PlanetSpecAPI spec = star.getSpec(); 648 Float val = SLIPSURGE_STRENGTH.get(spec.getPlanetType()); 649 if (val != null) return val; 650 651 if (spec.isGasGiant()) return 0f; 652 653 // probably a mod-added star of some sort 654 // time to make some guesses based on the "poorly understood factors" 655 656 String key = StarTypes.YELLOW; 657 658 String name = spec.getName().toLowerCase(); 659 if (name.contains("neutron") || spec.isPulsar()) { 660 key = StarTypes.NEUTRON_STAR; 661 } else if (name.contains("dwarf")) { 662 key = StarTypes.WHITE_DWARF; 663 } else if (name.contains("supergiant")) { 664 key = StarTypes.BLUE_SUPERGIANT; 665 } else if (name.contains("giant")) { 666 key = StarTypes.BLUE_GIANT; 667 } else if (name.contains(" hole")) { 668 key = StarTypes.BLACK_HOLE; 669 } else if (name.contains("brown")) { 670 key = StarTypes.BROWN_DWARF; 671 } 672 673 val = SLIPSURGE_STRENGTH.get(key); 674 if (val != null) return val; 675 return 0.4f; 676 } 677 678 679} 680 681 682 683 684