001package com.fs.starfarer.api.impl.campaign.intel.events.ht; 002 003import java.util.ArrayList; 004import java.util.EnumSet; 005import java.util.List; 006import java.util.Set; 007 008import java.awt.Color; 009 010import org.lwjgl.util.vector.Vector2f; 011 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.BattleAPI; 014import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 015import com.fs.starfarer.api.campaign.CampaignFleetAPI; 016import com.fs.starfarer.api.campaign.CargoAPI; 017import com.fs.starfarer.api.campaign.InteractionDialogAPI; 018import com.fs.starfarer.api.campaign.LocationAPI; 019import com.fs.starfarer.api.campaign.PersistentUIDataAPI.AbilitySlotAPI; 020import com.fs.starfarer.api.campaign.PersistentUIDataAPI.AbilitySlotsAPI; 021import com.fs.starfarer.api.campaign.SectorEntityToken; 022import com.fs.starfarer.api.campaign.SpecialItemData; 023import com.fs.starfarer.api.campaign.StarSystemAPI; 024import com.fs.starfarer.api.campaign.TextPanelAPI; 025import com.fs.starfarer.api.campaign.econ.Industry; 026import com.fs.starfarer.api.campaign.econ.MarketAPI; 027import com.fs.starfarer.api.campaign.listeners.CharacterStatsRefreshListener; 028import com.fs.starfarer.api.campaign.listeners.CurrentLocationChangedListener; 029import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 030import com.fs.starfarer.api.combat.MutableStat; 031import com.fs.starfarer.api.combat.MutableStat.StatMod; 032import com.fs.starfarer.api.combat.StatBonus; 033import com.fs.starfarer.api.impl.campaign.ids.Abilities; 034import com.fs.starfarer.api.impl.campaign.ids.Industries; 035import com.fs.starfarer.api.impl.campaign.ids.Items; 036import com.fs.starfarer.api.impl.campaign.ids.Stats; 037import com.fs.starfarer.api.impl.campaign.ids.Tags; 038import com.fs.starfarer.api.impl.campaign.intel.events.BaseEventIntel; 039import com.fs.starfarer.api.impl.campaign.intel.events.BaseFactorTooltip; 040import com.fs.starfarer.api.impl.campaign.intel.events.EventFactor; 041import com.fs.starfarer.api.impl.campaign.rulecmd.AddAbility; 042import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2; 043import com.fs.starfarer.api.ui.SectorMapAPI; 044import com.fs.starfarer.api.ui.TooltipMakerAPI; 045import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 046import com.fs.starfarer.api.util.Misc; 047import com.fs.starfarer.api.util.Misc.Token; 048import com.fs.starfarer.api.util.Misc.TokenType; 049import com.fs.starfarer.api.util.TimeoutTracker; 050 051public class HyperspaceTopographyEventIntel extends BaseEventIntel implements FleetEventListener, 052 CharacterStatsRefreshListener, 053 CurrentLocationChangedListener { 054 055 public static Color BAR_COLOR = Global.getSettings().getColor("progressBarFleetPointsColor"); 056 057 058// public static int PROGRESS_MAX = 1000; 059// public static int PROGRESS_1 = 100; 060// public static int PROGRESS_2 = 400; 061// public static int PROGRESS_3 = 700; 062 public static int PROGRESS_MAX = 1000; 063 public static int PROGRESS_1 = 100; 064 public static int PROGRESS_2 = 250; 065 public static int PROGRESS_3 = 400; 066 public static int PROGRESS_4 = 550; 067 public static int PROGRESS_5 = 700; 068 069 public static float BASE_DETECTION_RANGE_LY = 3f; 070 public static float RANGE_WITHIN_WHICH_SENSOR_ARRAYS_HELP_LY = 5f; 071 public static float RANGE_PER_DOMAIN_SENSOR_ARRAY = 2f; 072 public static float RANGE_PER_MAKESHIFT_SENSOR_ARRAY = 1f; 073 public static int MAX_SENSOR_ARRAYS = 3; 074 public static float WAYSTATION_BONUS = 2f; 075 076 077 public static float SLIPSTREAM_FUEL_MULT = 0.25f; 078 public static float HYPER_BURN_BONUS = 3f; 079 080 public static String KEY = "$hte_ref"; 081 082 public static enum Stage { 083 START, 084 SLIPSTREAM_DETECTION, 085 SLIPSTREAM_NAVIGATION, 086 REVERSE_POLARITY, 087 HYPERFIELD_OPTIMIZATION, 088 GENERATE_SLIPSURGE, 089 TOPOGRAPHIC_DATA, 090 } 091 092 093 public static float RECENT_READINGS_TIMEOUT = 30f; 094 public static float RECENT_READINGS_RANGE_LY = 10f; 095 096 public static class RecentTopographyReadings { 097 public Vector2f loc; 098 public RecentTopographyReadings(Vector2f loc) { 099 this.loc = loc; 100 } 101 } 102 103 public static void addFactorCreateIfNecessary(EventFactor factor, InteractionDialogAPI dialog) { 104 if (get() == null) { 105 //TextPanelAPI text = dialog == null ? null : dialog.getTextPanel(); 106 //new HyperspaceTopographyEventIntel(text); 107 // adding a factor anyway, so it'll show a message - don't need to double up 108 new HyperspaceTopographyEventIntel(null, false); 109 } 110 if (get() != null) { 111 get().addFactor(factor, dialog); 112 } 113 } 114 115 public static HyperspaceTopographyEventIntel get() { 116 return (HyperspaceTopographyEventIntel) Global.getSector().getMemoryWithoutUpdate().get(KEY); 117 } 118 119 120 protected TimeoutTracker<RecentTopographyReadings> recent = new TimeoutTracker<RecentTopographyReadings>(); 121 122// public static float CHECK_DAYS = 0.1f; 123// protected IntervalUtil interval = new IntervalUtil(CHECK_DAYS * 0.8f, CHECK_DAYS * 1.2f); 124// protected float burnBasedPoints = 0f; 125 126 public HyperspaceTopographyEventIntel(TextPanelAPI text, boolean withIntelNotification) { 127 super(); 128 129 Global.getSector().getMemoryWithoutUpdate().set(KEY, this); 130 131 132 setup(); 133 134 // now that the event is fully constructed, add it and send notification 135 Global.getSector().getIntelManager().addIntel(this, !withIntelNotification, text); 136 } 137 138 protected void setup() { 139 factors.clear(); 140 stages.clear(); 141 142 setMaxProgress(PROGRESS_MAX); 143 144 addStage(Stage.START, 0); 145 addStage(Stage.SLIPSTREAM_NAVIGATION, PROGRESS_1, StageIconSize.MEDIUM); 146 addStage(Stage.REVERSE_POLARITY, PROGRESS_2, StageIconSize.LARGE); 147 addStage(Stage.SLIPSTREAM_DETECTION, PROGRESS_3, StageIconSize.MEDIUM); 148 addStage(Stage.HYPERFIELD_OPTIMIZATION, PROGRESS_4, StageIconSize.MEDIUM); 149 addStage(Stage.GENERATE_SLIPSURGE, PROGRESS_5, StageIconSize.LARGE); 150 addStage(Stage.TOPOGRAPHIC_DATA, PROGRESS_MAX, true, StageIconSize.SMALL); 151 152 getDataFor(Stage.SLIPSTREAM_NAVIGATION).keepIconBrightWhenLaterStageReached = true; 153 getDataFor(Stage.SLIPSTREAM_DETECTION).keepIconBrightWhenLaterStageReached = true; 154 getDataFor(Stage.REVERSE_POLARITY).keepIconBrightWhenLaterStageReached = true; 155 getDataFor(Stage.HYPERFIELD_OPTIMIZATION).keepIconBrightWhenLaterStageReached = true; 156 getDataFor(Stage.GENERATE_SLIPSURGE).keepIconBrightWhenLaterStageReached = true; 157 158 } 159 160 protected Object readResolve() { 161 if (getDataFor(Stage.GENERATE_SLIPSURGE) == null) { 162 setup(); 163 } 164 return this; 165 } 166 167 168 @Override 169 protected void notifyEnding() { 170 super.notifyEnding(); 171 } 172 173 @Override 174 protected void notifyEnded() { 175 super.notifyEnded(); 176 Global.getSector().getMemoryWithoutUpdate().unset(KEY); 177 } 178 179 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode, boolean isUpdate, 180 Color tc, float initPad) { 181 182 if (addEventFactorBulletPoints(info, mode, isUpdate, tc, initPad)) { 183 return; 184 } 185 186 Color h = Misc.getHighlightColor(); 187 if (isUpdate && getListInfoParam() instanceof EventStageData) { 188 EventStageData esd = (EventStageData) getListInfoParam(); 189 if (esd.id == Stage.SLIPSTREAM_DETECTION) { 190 info.addPara("Able to detect slipstreams near your spaceports", tc, initPad); 191 } 192 if (esd.id == Stage.SLIPSTREAM_NAVIGATION) { 193// info.addPara("Fuel use while traversing slipstreams multiplied by %s", initPad, tc, 194// h, "" + SLIPSTREAM_FUEL_MULT + Strings.X); 195 info.addPara("Fuel use while traversing slipstreams reduced by %s", initPad, tc, 196 h, "" + (int)Math.round((1f - SLIPSTREAM_FUEL_MULT) * 100f) + "%"); 197 } 198 if (esd.id == Stage.HYPERFIELD_OPTIMIZATION) { 199 info.addPara("Maximum burn increased by %s while in hyperspace", initPad, tc, 200 h, "" + (int) HYPER_BURN_BONUS); 201 } 202 if (esd.id == Stage.REVERSE_POLARITY) { 203 info.addPara("%s ability unlocked", initPad, tc, h, "Reverse Polarity"); 204 } 205 if (esd.id == Stage.GENERATE_SLIPSURGE) { 206 info.addPara("%s ability unlocked", initPad, tc, h, "Generate Slipsurge"); 207 } 208 if (esd.id == Stage.TOPOGRAPHIC_DATA) { 209 info.addPara("Topographic data gained", tc, initPad); 210 } 211 return; 212 } 213 214// EventStageData esd = getLastActiveStage(false); 215// if (esd != null && EnumSet.of(Stage.START, Stage.HA_1, Stage.HA_2, Stage.HA_3, Stage.HA_4).contains(esd.id)) { 216// 217// } 218 } 219 220 public float getImageSizeForStageDesc(Object stageId) { 221// if (stageId == Stage.REVERSE_POLARITY || stageId == Stage.GENERATE_SLIPSURGE) { 222// return 48f; 223// } 224 if (stageId == Stage.START) { 225 return 64f; 226 } 227 return 48f; 228 } 229 public float getImageIndentForStageDesc(Object stageId) { 230// if (stageId == Stage.REVERSE_POLARITY || stageId == Stage.GENERATE_SLIPSURGE) { 231// return 16f; 232// } 233 if (stageId == Stage.START) { 234 return 0f; 235 } 236 return 16f; 237 } 238 239 @Override 240 public void addStageDescriptionText(TooltipMakerAPI info, float width, Object stageId) { 241 float opad = 10f; 242 float small = 0f; 243 Color h = Misc.getHighlightColor(); 244 245 //setProgress(0); 246 //setProgress(199); 247 //setProgress(600); 248 //setProgress(899); 249 //setProgress(1000); 250 //setProgress(499); 251 //setProgress(600); 252 253 EventStageData stage = getDataFor(stageId); 254 if (stage == null) return; 255 256// if (isStageActiveAndLast(stageId) && stageId == Stage.START) { 257// addStageDesc(info, stageId, small, false); 258// } else if (isStageActive(stageId) && stageId != Stage.START) { 259// addStageDesc(info, stageId, small, false); 260// } 261 262 if (isStageActive(stageId)) { 263 addStageDesc(info, stageId, small, false); 264 } 265 } 266 267 268 public void addStageDesc(TooltipMakerAPI info, Object stageId, float initPad, boolean forTooltip) { 269 float opad = 10f; 270 Color h = Misc.getHighlightColor(); 271 if (stageId == Stage.START) { 272 info.addPara("Detailed sensor readings greatly aid hyperspace navigation. " 273 + "There are many ways of acquiring this data, including using in-system sensor arrays," 274 + " using an Active Sensor Burst near interesting phenomena, " 275 + "traveling through hyperspace at a very high burn level, or simply buying the data from scavengers.", 276 initPad); 277 } else if (stageId == Stage.SLIPSTREAM_DETECTION) { 278// info.addPara("The facilities and staff at a Spaceport are able to interpret data from various sources " 279// + "to discover the presence of nearby slipstreams. The detection range is increased " 280// + "for larger colonies. Claimed sensor arrays within %s light-years provide an additional " 281// + "bonus - %s ly for Domain-era arrays, and %s ly for makeshift ones. " 282// + "Up to %s sensor arrays can be of use.", initPad, 283// Misc.getHighlightColor(), 284// "" + (int) RANGE_WITHIN_WHICH_SENSOR_ARRAYS_HELP_LY, 285// "+" + (int) RANGE_PER_DOMAIN_SENSOR_ARRAY, 286// "+" + (int) RANGE_PER_MAKESHIFT_SENSOR_ARRAY, 287// "" + (int) MAX_SENSOR_ARRAYS 288// ); 289 info.addPara("Allows a Spaceport " 290 + "to detect nearby slipstreams. Detection range increased " 291 + "for %s. Claimed sensor arrays within %s light-years provide extra detection range: " 292 + "%s ly for Domain-era arrays, and %s ly for makeshift ones. " 293 + "Up to %s sensor arrays can be used.", initPad, 294 Misc.getHighlightColor(), 295 "larger colonies", 296 "" + (int) RANGE_WITHIN_WHICH_SENSOR_ARRAYS_HELP_LY, 297 "+" + (int) RANGE_PER_DOMAIN_SENSOR_ARRAY, 298 "+" + (int) RANGE_PER_MAKESHIFT_SENSOR_ARRAY, 299 "" + (int) MAX_SENSOR_ARRAYS 300 ); 301 } else if (stageId == Stage.SLIPSTREAM_NAVIGATION) { 302 info.addPara("Fuel use while traveling inside slipstreams reduced by %s. This reduction is multiplicative " + 303 "with the baseline fuel use reduction for traveling inside a slipstream.", 304 initPad, h, 305 "" + (int)Math.round((1f - SLIPSTREAM_FUEL_MULT) * 100f) + "%"); 306 } else if (stageId == Stage.REVERSE_POLARITY) { 307 info.addPara("Unlocks the %s ability, which allows your fleet to " 308 + "travel against the current of slipstreams.", initPad, h, 309 "Reverse Polarity"); 310 } else if (stageId == Stage.GENERATE_SLIPSURGE) { 311 info.addPara("Unlocks the %s ability, which allows your fleet to " 312 + "create powerful, short-lived slipstreams useful for rapid travel.", initPad, h, 313 "Generate Slipsurge"); 314 } else if (stageId == Stage.HYPERFIELD_OPTIMIZATION) { 315 info.addPara("Maximum burn increased by %s while in hyperspace.", initPad, h, 316 "" + (int) HYPER_BURN_BONUS); 317 } else if (stageId == Stage.TOPOGRAPHIC_DATA) { 318 int min = getTopoResetMin(); 319 int max = getTopoResetMax(); 320 info.addPara("A batch of topographic data that can be sold for a" 321 + " considerable number of credits.", initPad); 322 info.addPara("Event progress will be reset to between %s and %s points when this outcome is reached.", 323 opad, h, "" + min, "" + max); 324 } 325 } 326 327 public TooltipCreator getStageTooltipImpl(Object stageId) { 328 final EventStageData esd = getDataFor(stageId); 329 330 if (esd != null && EnumSet.of(Stage.SLIPSTREAM_DETECTION, Stage.SLIPSTREAM_NAVIGATION, 331 Stage.GENERATE_SLIPSURGE, Stage.REVERSE_POLARITY, 332 Stage.HYPERFIELD_OPTIMIZATION, Stage.TOPOGRAPHIC_DATA).contains(esd.id)) { 333 return new BaseFactorTooltip() { 334 @Override 335 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 336 float opad = 10f; 337 338 if (esd.id == Stage.SLIPSTREAM_DETECTION) { 339 tooltip.addTitle("Slipstream detection"); 340 } else if (esd.id == Stage.SLIPSTREAM_NAVIGATION) { 341 tooltip.addTitle("Slipstream navigation"); 342 } else if (esd.id == Stage.HYPERFIELD_OPTIMIZATION) { 343 tooltip.addTitle("Hyperfield optimization"); 344 } else if (esd.id == Stage.REVERSE_POLARITY) { 345 tooltip.addTitle("Reverse Polarity"); 346 } else if (esd.id == Stage.GENERATE_SLIPSURGE) { 347 tooltip.addTitle("Generate Slipsurge"); 348 } else if (esd.id == Stage.TOPOGRAPHIC_DATA) { 349 tooltip.addTitle("Topographic data"); 350 } 351 352 addStageDesc(tooltip, esd.id, opad, true); 353 354 esd.addProgressReq(tooltip, opad); 355 } 356 }; 357 } 358 359 return null; 360 } 361 362 363 364 @Override 365 public String getIcon() { 366 return Global.getSettings().getSpriteName("events", "hyperspace_topography"); 367 } 368 369 protected String getStageIconImpl(Object stageId) { 370 EventStageData esd = getDataFor(stageId); 371 if (esd == null) return null; 372 373 374 if (EnumSet.of(Stage.SLIPSTREAM_DETECTION, Stage.SLIPSTREAM_NAVIGATION, Stage.HYPERFIELD_OPTIMIZATION, 375 Stage.TOPOGRAPHIC_DATA, Stage.START).contains(esd.id)) { 376 return Global.getSettings().getSpriteName("events", "hyperspace_topography_" + ((Stage)esd.id).name()); 377 } 378 if (stageId == Stage.REVERSE_POLARITY) { 379 return Global.getSettings().getAbilitySpec(Abilities.REVERSE_POLARITY).getIconName(); 380 } 381 if (stageId == Stage.GENERATE_SLIPSURGE) { 382 return Global.getSettings().getAbilitySpec(Abilities.GENERATE_SLIPSURGE).getIconName(); 383 } 384 // should not happen - the above cases should handle all possibilities - but just in case 385 return Global.getSettings().getSpriteName("events", "hyperspace_topography"); 386 } 387 388 389 @Override 390 public Color getBarColor() { 391 Color color = BAR_COLOR; 392 //color = Misc.getBasePlayerColor(); 393 color = Misc.interpolateColor(color, Color.black, 0.25f); 394 return color; 395 } 396 397 @Override 398 public Color getBarProgressIndicatorColor() { 399 return super.getBarProgressIndicatorColor(); 400 } 401 402 @Override 403 protected int getStageImportance(Object stageId) { 404 return super.getStageImportance(stageId); 405 } 406 407 408 @Override 409 protected String getName() { 410 return "Hyperspace Topography"; 411 } 412 413 414 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 415 416 } 417 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 418// if (isEnded() || isEnding()) return; 419// 420// if (!battle.isPlayerInvolved()) return; 421 422// HAShipsDestroyedFactor factor = new HAShipsDestroyedFactor(-1 * points); 423// sendUpdateIfPlayerHasIntel(factor, false); 424// addFactor(factor); 425 } 426 427 428 public int getTopoResetMin() { 429 EventStageData stage = getDataFor(Stage.GENERATE_SLIPSURGE); 430 return stage.progress; 431 } 432 public int getTopoResetMax() { 433 return getTopoResetMin() + 50; 434 } 435 436 public void resetTopographicData() { 437 int resetProgress = getTopoResetMin() + getRandom().nextInt(getTopoResetMax() - getTopoResetMin() + 1); 438 setProgress(resetProgress); 439 } 440 441 @Override 442 public Set<String> getIntelTags(SectorMapAPI map) { 443 Set<String> tags = super.getIntelTags(map); 444 tags.add(Tags.INTEL_EXPLORATION); 445 //tags.remove(Tags.INTEL_MAJOR_EVENT); 446 return tags; 447 } 448 449 @Override 450 protected void advanceImpl(float amount) { 451 super.advanceImpl(amount); 452 applyFleetEffects(); 453 454 float days = Global.getSector().getClock().convertToDays(amount); 455 recent.advance(days); 456 457 //setProgress(getProgress() + 10); 458 } 459 460 public void addAbility(String id) { 461 if (Global.getSector().getPlayerFleet().hasAbility(id)) { 462 return; 463 } 464 List<Token> params = new ArrayList<Token>(); 465 Token t = new Token(id, TokenType.LITERAL); 466 params.add(t); 467 t = new Token("-1", TokenType.LITERAL); 468 params.add(t); // don't want to assign it to a slot - will assign as hyper-only alternate later here 469 new AddAbility().execute(null, null, params, null); 470 471 472 AbilitySlotsAPI slots = Global.getSector().getUIData().getAbilitySlotsAPI(); 473 int curr = slots.getCurrBarIndex(); 474 OUTER: for (int i = 0; i < 5; i++) { 475 slots.setCurrBarIndex(i); 476 for (AbilitySlotAPI slot : slots.getCurrSlotsCopy()) { 477 if (Abilities.REVERSE_POLARITY.equals(id) && Abilities.SCAVENGE.equals(slot.getAbilityId())) { 478 slot.setInHyperAbilityId(Abilities.REVERSE_POLARITY); 479 break OUTER; 480 } 481 if (Abilities.GENERATE_SLIPSURGE.equals(id) && Abilities.DISTRESS_CALL.equals(slot.getAbilityId())) { 482 slot.setInHyperAbilityId(Abilities.GENERATE_SLIPSURGE); 483 break OUTER; 484 } 485 } 486 } 487 slots.setCurrBarIndex(curr); 488 } 489 490 491 @Override 492 protected void notifyStageReached(EventStageData stage) { 493 //applyFleetEffects(); 494 495 if (stage.id == Stage.REVERSE_POLARITY) { 496 addAbility(Abilities.REVERSE_POLARITY); 497 } 498 if (stage.id == Stage.GENERATE_SLIPSURGE) { 499 addAbility(Abilities.GENERATE_SLIPSURGE); 500 } 501 if (stage.id == Stage.TOPOGRAPHIC_DATA) { 502 resetTopographicData(); 503 504 CargoAPI cargo = Global.getSector().getPlayerFleet().getCargo(); 505 cargo.addSpecial(new SpecialItemData(Items.TOPOGRAPHIC_DATA, null), 1); 506 //sendUpdateIfPlayerHasIntel(stage, getTextPanelForStageChange()); 507 } 508 } 509 510 public void reportCurrentLocationChanged(LocationAPI prev, LocationAPI curr) { 511 //applyFleetEffects(); 512 } 513 514 public void reportAboutToRefreshCharacterStatEffects() { 515 516 } 517 518 public void reportRefreshedCharacterStatEffects() { 519 // called when opening colony screen, so the Spaceport tooltip gets the right values 520 updateMarketDetectionRanges(); 521 applyFleetEffects(); 522 } 523 524 public void applyFleetEffects() { 525 String id1 = "hypertopology1"; 526 527 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 528 pf.getStats().getFleetwideMaxBurnMod().unmodifyFlat(id1); 529 530 MutableStat stat = pf.getStats().getDynamic().getStat(Stats.FUEL_USE_NOT_SHOWN_ON_MAP_MULT); 531 stat.unmodifyMult(id1); 532 533 //if (pf.isInHyperspace()) { // doesn't work; after reportCurrentLocationChanged() 534 // the current location is right but the player fleet hasn't been added to it yet 535 if (Global.getSector().getCurrentLocation().isHyperspace()) { 536 if (isStageActive(Stage.SLIPSTREAM_NAVIGATION)) { 537 for (StatMod mod : stat.getMultMods().values()) { 538 if (SlipstreamTerrainPlugin2.FUEL_USE_MODIFIER_DESC.equals(mod.desc)) { 539 stat.modifyMult(id1, SLIPSTREAM_FUEL_MULT, 540 SlipstreamTerrainPlugin2.FUEL_USE_MODIFIER_DESC + " (hyperspace topography)"); 541 break; 542 } 543 } 544 } 545 546 if (isStageActive(Stage.HYPERFIELD_OPTIMIZATION)) { 547 pf.getStats().getFleetwideMaxBurnMod().modifyFlat(id1, HYPER_BURN_BONUS, "Hyperspace topography"); 548 } 549 550 } 551 } 552 553 public void updateMarketDetectionRanges() { 554 if (isStageActive(Stage.SLIPSTREAM_DETECTION)) { 555 String id1 = "hypertopology1"; 556 String id2 = "hypertopology2"; 557 String id3 = "hypertopology3"; 558 String id4 = "hypertopology4"; 559 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 560 if (market.isHidden()) continue; 561 562 boolean unapplicable = false; 563 Industry spaceport = market.getIndustry(Industries.SPACEPORT); 564 if (spaceport == null) { 565 spaceport = market.getIndustry(Industries.MEGAPORT); 566 } 567 if (spaceport == null || !spaceport.isFunctional()) { 568 unapplicable = true; 569 } 570 571 StatBonus mod = market.getStats().getDynamic().getMod(Stats.SLIPSTREAM_REVEAL_RANGE_LY_MOD); 572 if (!market.isPlayerOwned() || unapplicable) { 573 mod.unmodify(id1); 574 mod.unmodify(id2); 575 mod.unmodify(id3); 576 mod.unmodify(id4); 577 continue; 578 } 579 580 mod.modifyFlat(id1, BASE_DETECTION_RANGE_LY, "Base detection range"); 581 mod.modifyFlat(id2, market.getSize(), "Colony size"); 582 583 float arraysBonus = gerSensorArrayBonusFor(market, RANGE_WITHIN_WHICH_SENSOR_ARRAYS_HELP_LY); 584 585 mod.modifyFlatAlways(id3, arraysBonus, 586 "Claimed sensor arrays within " + (int) RANGE_WITHIN_WHICH_SENSOR_ARRAYS_HELP_LY + 587 " ly (max: " + (int) MAX_SENSOR_ARRAYS + " arrays)"); 588 } 589 } 590 } 591 592 public float gerSensorArrayBonusFor(MarketAPI market, float range) { 593 int countDomain = 0; 594 int countMakeshift= 0; 595 Vector2f locInHyper = market.getLocationInHyperspace(); 596 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 597 float dist = Misc.getDistanceLY(locInHyper, system.getLocation()); 598 if (dist > range && Math.round(dist * 10f) <= range * 10f) { 599 dist = range; 600 } 601 if (dist <= range) { 602 for (SectorEntityToken entity : system.getEntitiesWithTag(Tags.SENSOR_ARRAY)) { 603 if (entity.getFaction() != null && entity.getFaction().isPlayerFaction()) { 604 if (entity.hasTag(Tags.MAKESHIFT)) { 605 countMakeshift++; 606 } else { 607 countDomain++; 608 } 609 } 610 } 611 } 612 } 613 614 float bonus = Math.min(countDomain, MAX_SENSOR_ARRAYS) * RANGE_PER_DOMAIN_SENSOR_ARRAY; 615 float useMakeshift = Math.min(MAX_SENSOR_ARRAYS - countDomain, countMakeshift); 616 if (useMakeshift < 0) useMakeshift = 0; 617 bonus += useMakeshift * RANGE_PER_MAKESHIFT_SENSOR_ARRAY; 618 //bonus += Math.min(Math.max(0, countMakeshift - countDomain), MAX_SENSOR_ARRAYS) * RANGE_PER_MAKESHIFT_SENSOR_ARRAY; 619 620 return bonus; 621 } 622 623 public boolean withMonthlyFactors() { 624 return false; 625 } 626 627 628 public void addRecentReadings(Vector2f loc) { 629 recent.add(new RecentTopographyReadings(loc), RECENT_READINGS_TIMEOUT); 630 } 631 632 public static boolean hasRecentReadingsNearPlayer() { 633 return get() != null && get().hasRecentReadingsNear(Global.getSector().getPlayerFleet().getLocationInHyperspace()); 634 } 635 636 public boolean hasRecentReadingsNear(Vector2f loc) { 637 for (RecentTopographyReadings curr : recent.getItems()) { 638 float distLY = Misc.getDistanceLY(loc, curr.loc); 639 if (distLY <= RECENT_READINGS_RANGE_LY) { 640 return true; 641 } 642 } 643 return false; 644 } 645 646 protected String getSoundForStageReachedUpdate(Object stageId) { 647 if (stageId == Stage.REVERSE_POLARITY || stageId == Stage.GENERATE_SLIPSURGE) { 648 return "ui_learned_ability"; 649 } 650 return super.getSoundForStageReachedUpdate(stageId); 651 } 652 653 @Override 654 protected String getSoundForOneTimeFactorUpdate(EventFactor factor) { 655// if (factor instanceof HTAbyssalLightFactor) { 656// return "sound_none"; 657// } 658 return null; 659 } 660 661 662 663} 664 665 666 667 668 669 670 671