001package com.fs.starfarer.api.impl.campaign.intel.events; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.Iterator; 007import java.util.LinkedHashMap; 008import java.util.List; 009import java.util.Map; 010import java.util.Set; 011 012import java.awt.Color; 013 014import com.fs.starfarer.api.Global; 015import com.fs.starfarer.api.campaign.BattleAPI; 016import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 017import com.fs.starfarer.api.campaign.CampaignFleetAPI; 018import com.fs.starfarer.api.campaign.StarSystemAPI; 019import com.fs.starfarer.api.campaign.StoryPointActionDelegate; 020import com.fs.starfarer.api.campaign.econ.EconomyAPI.EconomyUpdateListener; 021import com.fs.starfarer.api.campaign.econ.MarketAPI; 022import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 023import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 024import com.fs.starfarer.api.combat.MutableStatWithTempMods; 025import com.fs.starfarer.api.fleet.FleetMemberAPI; 026import com.fs.starfarer.api.impl.campaign.command.WarSimScript.LocationDanger; 027import com.fs.starfarer.api.impl.campaign.ids.Conditions; 028import com.fs.starfarer.api.impl.campaign.ids.Factions; 029import com.fs.starfarer.api.impl.campaign.ids.Sounds; 030import com.fs.starfarer.api.impl.campaign.ids.Tags; 031import com.fs.starfarer.api.impl.campaign.intel.bases.LuddicPathBaseIntel; 032import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel; 033import com.fs.starfarer.api.impl.campaign.rulecmd.HA_CMD; 034import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.BaseOptionStoryPointActionDelegate; 035import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.StoryOptionParams; 036import com.fs.starfarer.api.ui.Alignment; 037import com.fs.starfarer.api.ui.ButtonAPI; 038import com.fs.starfarer.api.ui.IntelUIAPI; 039import com.fs.starfarer.api.ui.LabelAPI; 040import com.fs.starfarer.api.ui.SectorMapAPI; 041import com.fs.starfarer.api.ui.TooltipMakerAPI; 042import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 043import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipLocation; 044import com.fs.starfarer.api.util.Misc; 045import com.fs.starfarer.api.util.WeightedRandomPicker; 046 047public class HostileActivityEventIntel extends BaseEventIntel implements EconomyUpdateListener, FleetEventListener { 048 049 public static enum Stage { 050 START, 051 MINOR_EVENT, 052 HA_EVENT, 053 054 // unused, left in for save compatibility between 0.96a and 0.96.1a, can remove for 0.97a 055 @Deprecated HA_1, 056 @Deprecated HA_2, 057 @Deprecated INCREASED_DEFENSES, 058 @Deprecated HA_3, 059 @Deprecated HA_4, 060 } 061 public static String KEY = "$hae_ref"; 062 063 public static String BUTTON_ESCALATE = "button_escalate"; 064 065 public static float FP_PER_POINT = Global.getSettings().getFloat("HA_fleetPointsPerPoint"); 066 067 public static int MAX_PROGRESS = 600; 068 public static int ESCALATE_PROGRESS = 550; 069 070 public static int RESET_MIN = 0; 071 public static int RESET_MAX = 400; 072 073 074 public static class HAERandomEventData { 075 public HostileActivityFactor factor; 076 public EventStageData stage; 077 public boolean isReset = false; 078 public Object custom; 079 public HAERandomEventData(HostileActivityFactor factor, EventStageData stage) { 080 this.factor = factor; 081 this.stage = stage; 082 } 083 084 } 085 086 public static class HAEFactorDangerData { 087 public HostileActivityFactor factor; 088 public float mag; 089 } 090 public static class HAEStarSystemDangerData { 091 public StarSystemAPI system; 092 public float maxMag; 093 public float totalMag; 094 public float sortMag; 095 public List<HAEFactorDangerData> factorData = new ArrayList<HAEFactorDangerData>(); 096 } 097 098 099 public static HostileActivityEventIntel get() { 100 return (HostileActivityEventIntel) Global.getSector().getMemoryWithoutUpdate().get(KEY); 101 } 102 103 protected int blowback; 104 protected Map<String, MutableStatWithTempMods> systemSpawnMults = new LinkedHashMap<String, MutableStatWithTempMods>(); 105 106 public HostileActivityEventIntel() { 107 super(); 108 109 //Global.getSector().getEconomy().addUpdateListener(this); 110 111 Global.getSector().getMemoryWithoutUpdate().set(KEY, this); 112 113 setup(); 114 115 // now that the event is fully constructed, add it and send notification 116 Global.getSector().getIntelManager().addIntel(this); 117 } 118 119 protected void setup() { 120 121 boolean minorCompleted = false; 122 EventStageData minor = getDataFor(Stage.MINOR_EVENT); 123 if (minor != null) minorCompleted = minor.wasEverReached; 124 125 factors.clear(); 126 stages.clear(); 127 128 setMaxProgress(MAX_PROGRESS); 129 addStage(Stage.START, 0); 130 addStage(Stage.MINOR_EVENT, 300, StageIconSize.MEDIUM); 131 addStage(Stage.HA_EVENT, 600, true, StageIconSize.LARGE); 132 133 setRandomized(Stage.MINOR_EVENT, RandomizedStageType.BAD, 200, 250, false, false); 134 setRandomized(Stage.HA_EVENT, RandomizedStageType.BAD, 425, 500, false); 135 136 minor = getDataFor(Stage.MINOR_EVENT); 137 if (minor != null) { 138 minor.wasEverReached = minorCompleted; 139 } 140 141 Global.getSector().getListenerManager().removeListenerOfClass(PirateHostileActivityFactor.class); 142 Global.getSector().getListenerManager().removeListenerOfClass(LuddicPathHostileActivityFactor.class); 143 Global.getSector().getListenerManager().removeListenerOfClass(PerseanLeagueHostileActivityFactor.class); 144 Global.getSector().getListenerManager().removeListenerOfClass(TriTachyonHostileActivityFactor.class); 145 Global.getSector().getListenerManager().removeListenerOfClass(LuddicChurchHostileActivityFactor.class); 146 Global.getSector().getListenerManager().removeListenerOfClass(SindrianDiktatHostileActivityFactor.class); 147 Global.getSector().getListenerManager().removeListenerOfClass(HegemonyHostileActivityFactor.class); 148 Global.getSector().getListenerManager().removeListenerOfClass(RemnantHostileActivityFactor.class); 149 150 151 addFactor(new HAColonyDefensesFactor()); 152 addFactor(new HAShipsDestroyedFactorHint()); 153 154 addFactor(new HABlowbackFactor()); 155 156 PirateHostileActivityFactor pirate = new PirateHostileActivityFactor(this); 157 addActivity(pirate, new KantasProtectionPirateActivityCause2(this)); 158 addActivity(pirate, new StandardPirateActivityCause2(this)); 159 addActivity(pirate, new PirateBasePirateActivityCause2(this)); 160 addActivity(pirate, new KantasWrathPirateActivityCause2(this)); 161 162 LuddicPathHostileActivityFactor path = new LuddicPathHostileActivityFactor(this); 163 addActivity(path, new LuddicPathAgreementHostileActivityCause2(this)); 164 addActivity(path, new StandardLuddicPathActivityCause2(this)); 165 166 addActivity(new PerseanLeagueHostileActivityFactor(this), new StandardPerseanLeagueActivityCause(this)); 167 addActivity(new TriTachyonHostileActivityFactor(this), new TriTachyonStandardActivityCause(this)); 168 addActivity(new LuddicChurchHostileActivityFactor(this), new LuddicChurchStandardActivityCause(this)); 169 addActivity(new SindrianDiktatHostileActivityFactor(this), new SindrianDiktatStandardActivityCause(this)); 170 addActivity(new HegemonyHostileActivityFactor(this), new HegemonyAICoresActivityCause(this)); 171 addActivity(new RemnantHostileActivityFactor(this), new RemnantNexusActivityCause(this)); 172 173 ListenerUtil.finishedAddingCrisisFactors(this); 174 } 175 176 177 protected Object readResolve() { 178 if (systemSpawnMults == null) { 179 systemSpawnMults = new LinkedHashMap<String, MutableStatWithTempMods>(); 180 } 181 return this; 182 } 183 184 public void redoSetupIfNeeded() { 185 if (getDataFor(Stage.INCREASED_DEFENSES) != null || getMaxProgress() == 500) {// || Global.getSettings().isDevMode()) { 186 setup(); 187 } 188 } 189 190 191 @Override 192 protected void notifyEnding() { 193 super.notifyEnding(); 194 Global.getSector().getEconomy().removeUpdateListener(this); 195 cleanUpHostileActivityConditions(); 196 } 197 198 @Override 199 protected void notifyEnded() { 200 super.notifyEnded(); 201 Global.getSector().getMemoryWithoutUpdate().unset(KEY); 202 } 203 204 205 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode, boolean isUpdate, 206 Color tc, float initPad) { 207 208 if (addEventFactorBulletPoints(info, mode, isUpdate, tc, initPad)) { 209 return; 210 } 211 212 if (isUpdate && getListInfoParam() instanceof HAERandomEventData) { 213 HAERandomEventData data = (HAERandomEventData) getListInfoParam(); 214 if (data.isReset) { 215 data.factor.addBulletPointForEventReset(this, data.stage, info, mode, isUpdate, tc, initPad); 216 } else { 217 data.factor.addBulletPointForEvent(this, data.stage, info, mode, isUpdate, tc, initPad); 218 } 219 return; 220 } 221 222 for (EventStageData stage : stages) { 223 if (stage.rollData instanceof HAERandomEventData) { 224 HAERandomEventData data = (HAERandomEventData) stage.rollData; 225 data.factor.addBulletPointForEvent(this, stage, info, mode, isUpdate, tc, initPad); 226 return; 227 } 228 } 229 230// EventStageData esd = getLastActiveStage(false); 231// if (esd != null && EnumSet.of(Stage.START, Stage.HA_1, Stage.HA_2, Stage.HA_3, Stage.HA_4).contains(esd.id)) { 232// Pair<String, Color> p = getImpactDisplayData((Stage) esd.id); 233// String impact = p.one; 234// Color impactColor = p.two; 235//// if (p.one.equals("extreme")) { 236//// System.out.println("wefwefwe"); 237//// } 238// info.addPara("Colony impact: %s", initPad, tc, impactColor, impact); 239// return; 240// } 241 242 //super.addBulletPoints(info, mode, isUpdate, tc, initPad); 243 } 244 245 public HAERandomEventData getRollDataForEvent() { 246 EventStageData stage = getDataFor(Stage.HA_EVENT); 247 if (stage == null) return null;; 248 249 if (stage.rollData instanceof HAERandomEventData) { 250 HAERandomEventData data = (HAERandomEventData) stage.rollData; 251 return data; 252 } 253 return null; 254 } 255 256 @Override 257 public void addStageDescriptionText(TooltipMakerAPI info, float width, Object stageId) { 258 float opad = 10f; 259 float small = 0f; 260 Color h = Misc.getHighlightColor(); 261 262 //setProgress(0); 263 //setProgress(210); 264 //setProgress(600); 265 //setProgress(899); 266// setProgress(424); 267// setProgress(480); 268// setProgress(230); 269// random = new Random(); 270// setProgress(260); 271// random = new Random(); 272// setProgress(499); 273 274 List<HAEStarSystemDangerData> systemData = computePlayerSystemDangerData(); 275 276 EventStageData stage = getDataFor(stageId); 277 if (stage == null) return; 278 279 if (stage.rollData instanceof HAERandomEventData) { 280 HAERandomEventData data = (HAERandomEventData) stage.rollData; 281 data.factor.addStageDescriptionForEvent(this, stage, info); 282 return; 283 } 284 285 if (isStageActiveAndLast(stageId)) { 286 if (stageId == Stage.START) { 287 float delta = getMonthlyProgress(); 288 289 if (delta <= 0) { 290 info.addPara("Low-level pirate activity continues, but aside from that, there are " 291 + "no major crises on the horizon.", small); 292 } else { 293 info.addPara("A crisis is virtually inevitable at some point, " 294 + "and hostile fleets continually probe your defenses, but where there is " 295 + "danger, there is often opportunity.", small); 296 } 297// info.addPara("A crisis is virtually inevitable at some point, " 298// + "and hostile fleets continually probe your defenses, but where there is " 299// + "danger, there is often opportunity. A crisis may be averted or delayed by defeating " 300// + "hostile fleets and taking other actions to address the various contributing factors, " 301// + "but another crisis will always be just beyond the horizon.", small); 302 } 303 304 float systemW = 230f; 305 float threatW = 300f; 306 info.beginTable(getFactionForUIColors(), 20f, 307 "Star system", systemW, 308 "Danger", 100f, 309 "Primary threats", threatW 310 ); 311 info.makeTableItemsClickable(); 312 313 int maxSystemsToList = 4; 314 int numListed = 0; 315 info.addTableHeaderTooltip(0, "Star system with hostile activity, and the name (or number) of your colonies found there.\n\nUp to four of the hardest-hit systems are listed here."); 316 info.addTableHeaderTooltip(1, "Danger level of the stronger fleets likely to be found in the system. " 317 + "Approximate, there may be exceptions. Does not include hostile fleets that may be present there for other reasons."); 318 info.addTableHeaderTooltip(2, "The most dangerous types of threats likely to be found in the system. " 319 + "Not comprehensive, and does not include hostile fleets that may be present there for other reasons."); 320 321 //List<HAEStarSystemDangerData> systemData = computePlayerSystemDangerData(); 322 323 for (final HAEStarSystemDangerData sys : systemData) { 324 if (sys.sortMag <= 0) continue; 325 326 float mag = sys.sortMag; 327 String danger = getDangerString(mag); 328 329 int maxThreats = 3; 330 int count = 0; 331 List<String> threats = new ArrayList<String>(); 332 List<Color> colors = new ArrayList<Color>(); 333 for (HAEFactorDangerData data : sys.factorData) { 334 if (data.mag <= 0) continue; 335 threats.add(data.factor.getNameForThreatList(count == 0)); 336 colors.add(data.factor.getNameColorForThreatList()); 337 count++; 338 if (count >= maxThreats) { 339 break; 340 } 341 } 342 String threatStr = Misc.getJoined("", threats); 343 344 LabelAPI label = info.createLabel(threatStr, Misc.getTextColor(), threatW); 345 label.setHighlightColors(colors.toArray(new Color[0])); 346 label.setHighlight(threats.toArray(new String[0])); 347 348 String systemName = sys.system.getNameWithNoType(); 349 //String systemName = sys.system.getNameWithLowercaseTypeShort(); 350 List<MarketAPI> colonies = Misc.getMarketsInLocation(sys.system, Factions.PLAYER); 351 String colStr = ""; 352 if (colonies.size() == 1) { 353 colStr = colonies.get(0).getName(); 354 } else { 355 colStr = "" + colonies.size() + " colonies"; 356 } 357 systemName += " - " + colStr + ""; 358 359 info.addRowWithGlow(Alignment.LMID, Misc.getBasePlayerColor(), systemName, 360 Alignment.MID, getDangerColor(mag), danger, 361 Alignment.MID, null, label); 362 info.addTooltipToAddedRow(new BaseFactorTooltip() { 363 @Override 364 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 365 float w = tooltip.getWidthSoFar(); 366 float h = Math.round(w / 1.6f); 367 tooltip.addSectorMap(w, h, sys.system, 0f); 368 tooltip.addPara("Click to open map", Misc.getGrayColor(), 5f); 369 } 370 371 }, TooltipLocation.LEFT, false); 372 373 info.setIdForAddedRow(sys); 374 375 numListed++; 376 if (numListed >= maxSystemsToList) { 377 break; 378 } 379 } 380 info.addTable("None", -1, opad); 381 info.addSpacer(3f); 382 } 383 } 384 385 386 387 @Override 388 public void afterStageDescriptions(TooltipMakerAPI main) { 389 int progress = getProgress(); 390 if (progress < ESCALATE_PROGRESS) { 391 float width = getBarWidth(); 392 Color color = Misc.getStoryOptionColor(); 393 Color dark = Misc.getStoryDarkColor(); 394 float bw = 300f; 395 ButtonAPI button = addGenericButton(main, bw, color, dark, "Escalate crisis", BUTTON_ESCALATE); 396 float inset = width - bw; 397 //inset = 0f; 398 button.getPosition().setXAlignOffset(inset); 399 main.addSpacer(0f).getPosition().setXAlignOffset(-inset); 400 if (progress >= ESCALATE_PROGRESS) { 401 button.setEnabled(false); 402 main.addTooltipTo(new TooltipCreator() { 403 @Override 404 public boolean isTooltipExpandable(Object tooltipParam) { 405 return false; 406 } 407 @Override 408 public float getTooltipWidth(Object tooltipParam) { 409 return 450; 410 } 411 @Override 412 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 413 tooltip.addPara("Only available when event progress is below %s points.", 0f, 414 Misc.getHighlightColor(), "" + (int) ESCALATE_PROGRESS); 415 } 416 }, button, TooltipLocation.BELOW); 417 } 418 } 419 } 420 421 public void tableRowClicked(IntelUIAPI ui, TableRowClickData data) { 422 if (data.rowId instanceof HAEStarSystemDangerData) { 423 HAEStarSystemDangerData d = (HAEStarSystemDangerData) data.rowId; 424 List<MarketAPI> m = Misc.getMarketsInLocation(d.system, Factions.PLAYER); 425 if (m.size() == 1) { 426 ui.showOnMap(m.get(0).getPrimaryEntity()); 427 } else { 428 ui.showOnMap(d.system.getHyperspaceAnchor()); 429 } 430 } 431 } 432 433 434 public TooltipCreator getStageTooltipImpl(Object stageId) { 435 final EventStageData esd = getDataFor(stageId); 436 437 if (esd != null && esd.rollData instanceof HAERandomEventData) { 438 HAERandomEventData data = (HAERandomEventData) esd.rollData; 439 return data.factor.getStageTooltipImpl(this, esd); 440 } 441 442 return null; 443 } 444 445 446 447 @Override 448 public String getIcon() { 449 return Global.getSettings().getSpriteName("events", "hostile_activity"); 450 } 451 452 453 454 @Override 455 protected String getStageIcon(Object stageId) { 456 EventStageData esd = getDataFor(stageId); 457 if (esd != null && esd.id == Stage.HA_EVENT && esd.rollData != null && RANDOM_EVENT_NONE.equals(esd.rollData)) { 458 return Global.getSettings().getSpriteName("events", "stage_unknown_neutral"); 459 } 460 return super.getStageIcon(stageId); 461 } 462 463 @Override 464 public TooltipCreator getStageTooltip(Object stageId) { 465 final EventStageData esd = getDataFor(stageId); 466 if (esd != null && esd.id == Stage.HA_EVENT && esd.rollData != null && RANDOM_EVENT_NONE.equals(esd.rollData)) { 467 return new TooltipCreator() { 468 public boolean isTooltipExpandable(Object tooltipParam) { 469 return false; 470 } 471 public float getTooltipWidth(Object tooltipParam) { 472 return BaseEventFactor.TOOLTIP_WIDTH; 473 } 474 475 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 476 Color h = Misc.getHighlightColor(); 477 tooltip.addPara("There's no crisis on the horizon right now. When this stage is reached, " 478 + "event progress will be reset to a lower value.", 479 0f); 480 } 481 }; 482 } 483 return super.getStageTooltip(stageId); 484 } 485 486 487 protected String getStageIconImpl(Object stageId) { 488 EventStageData esd = getDataFor(stageId); 489 if (esd == null) return null; 490 491// if (esd.id == Stage.MINOR_EVENT) { 492// System.out.println("ewfwfew"); 493// } 494 495 //setProgress(48); 496 //setProgress(74); 497 498 if (esd.rollData instanceof HAERandomEventData) { 499 HAERandomEventData data = (HAERandomEventData) esd.rollData; 500 return data.factor.getEventStageIcon(this, esd); 501 } 502// if (esd.id == Stage.HA_EVENT) { 503// System.out.println("wefwefwe"); 504// } 505 //if (stageId == Stage.START) return null; 506 507 if (esd.id == Stage.START) { 508 return Global.getSettings().getSpriteName("events", "hostile_activity_" + ((Stage)esd.id).name()); 509 } 510 // should not happen - the above cases should handle all possibilities - but just in case 511 return Global.getSettings().getSpriteName("events", "hostile_activity"); 512 } 513 514 515 @Override 516 public Color getBarColor() { 517 Color color = Misc.getNegativeHighlightColor(); 518 //color = Misc.getBasePlayerColor(); 519 color = Misc.interpolateColor(color, Color.black, 0.25f); 520 return color; 521 } 522 523 @Override 524 public Color getBarProgressIndicatorColor() { 525 return super.getBarProgressIndicatorColor(); 526 } 527 528 529 @Override 530 protected int getStageImportance(Object stageId) { 531// if (stageId == Stage.HA_EVENT) { 532// return 1; 533// } 534// if (stageId == Stage.MINOR_EVENT) { 535// return 1; 536// } 537 return super.getStageImportance(stageId); 538 } 539 540 541 542 @Override 543 protected String getName() { 544 //return "Hostile Activity"; 545 return "Colony Crises"; 546 } 547 548 549 public boolean isEventProgressANegativeThingForThePlayer() { 550 return true; 551 } 552 553 554 public void addActivity(BaseHostileActivityFactor factor, HostileActivityCause2 cause) { 555 BaseHostileActivityFactor curr = getActivityOfClass(factor.getClass()); 556 if (curr == null) { 557 addFactor(factor); 558 curr = factor; 559 } 560 curr.addCause(cause); 561 } 562 563 564 public void removeActivityCause(Class activityClass, Class causeClass) { 565 BaseHostileActivityFactor curr = getActivityOfClass(activityClass); 566 if (curr == null) return; 567 568 HostileActivityCause2 cause = curr.getCauseOfClass(causeClass); 569 if (cause == null) return; 570 571 curr.getCauses().remove(cause); 572 573 if (curr.getCauses().isEmpty()) { 574 removeActivity(curr); 575 } 576 } 577 578 public HostileActivityCause2 getActivityCause(Class activityClass, Class causeClass) { 579 BaseHostileActivityFactor curr = getActivityOfClass(activityClass); 580 if (curr == null) return null; 581 582 HostileActivityCause2 cause = curr.getCauseOfClass(causeClass); 583 if (cause == null) return null; 584 585 return cause; 586 } 587 588 589 public void removeActivity(BaseHostileActivityFactor plugin) { 590 factors.remove(plugin); 591 } 592 public void removeActivityOfClass(Class c) { 593 Iterator<EventFactor> iter = factors.iterator(); 594 while (iter.hasNext()) { 595 EventFactor curr = iter.next(); 596 if (curr.getClass() == c) { 597 iter.remove(); 598 } 599 } 600 } 601 602 public BaseHostileActivityFactor getActivityOfClass(Class c) { 603 Iterator<EventFactor> iter = factors.iterator(); 604 while (iter.hasNext()) { 605 EventFactor curr = iter.next(); 606 if (curr.getClass() == c) { 607 return (BaseHostileActivityFactor) curr; 608 } 609 } 610 return null; 611 } 612 613 public List<HAEStarSystemDangerData> computePlayerSystemDangerData() { 614 List<HAEStarSystemDangerData> systemData = new ArrayList<HAEStarSystemDangerData>(); 615 for (StarSystemAPI system : Misc.getPlayerSystems(false)) { 616 HAEStarSystemDangerData data = computeDangerData(system); 617 systemData.add(data); 618 } 619 Collections.sort(systemData, new Comparator<HAEStarSystemDangerData>() { 620 public int compare(HAEStarSystemDangerData o1, HAEStarSystemDangerData o2) { 621 int result = (int) Math.signum(o2.sortMag - o1.sortMag); 622 if (result == 0) { 623 result = (int) Math.signum(o2.totalMag - o1.totalMag); 624 } 625 if (result == 0) { 626 result = (int) Math.signum(o2.system.getId().hashCode() - o1.system.getId().hashCode()); 627 } 628 return result; 629 } 630 }); 631 return systemData; 632 } 633 634 public HAEStarSystemDangerData computeDangerData(StarSystemAPI system) { 635 HAEStarSystemDangerData data = new HAEStarSystemDangerData(); 636 data.system = system; 637 638 float maxMag = 0f; 639 float total = 0f; 640 for (EventFactor factor : factors) { 641 if (factor instanceof BaseHostileActivityFactor) { 642 HAEFactorDangerData curr = new HAEFactorDangerData(); 643 curr.factor = (HostileActivityFactor) factor; 644 curr.mag = ((BaseHostileActivityFactor) factor).getEffectMagnitude(system); 645 data.factorData.add(curr); 646 maxMag = Math.max(maxMag, curr.mag); 647 total += curr.mag; 648 } 649 } 650 data.maxMag = maxMag; 651 data.totalMag = total; 652 data.sortMag = data.maxMag * 0.75f + data.totalMag * 0.25f; 653 654 Collections.sort(data.factorData, new Comparator<HAEFactorDangerData>() { 655 public int compare(HAEFactorDangerData o1, HAEFactorDangerData o2) { 656 return (int) Math.signum(o2.mag - o1.mag); 657 } 658 }); 659 660 return data; 661 } 662 663 public LocationDanger getDanger(float mag) { 664 if (mag <= 0f) return LocationDanger.NONE; 665 if (mag < 0.25f) return LocationDanger.MINIMAL; 666 if (mag < 0.5f) return LocationDanger.LOW; 667 if (mag < 0.75f) return LocationDanger.MEDIUM; 668 if (mag < 1f) return LocationDanger.HIGH; 669 return LocationDanger.EXTREME; 670 } 671 672 public String getDangerString(float mag) { 673 return getDangerString(getDanger(mag)); 674 } 675 public String getDangerString(LocationDanger d) { 676 switch (d) { 677 case EXTREME: return "Extreme"; 678 case HIGH: return "High"; 679 case MEDIUM: return "Medium"; 680 case LOW: return "Low"; 681 case MINIMAL: return "Minimal"; 682 case NONE: return "None"; 683 } 684 return "Unknown"; 685 } 686 687 public Color getDangerColor(float mag) { 688 LocationDanger d = getDanger(mag); 689 if (d == LocationDanger.NONE || d == LocationDanger.MINIMAL) { 690 return Misc.getPositiveHighlightColor(); 691 } 692 if (d == LocationDanger.EXTREME || d == LocationDanger.HIGH) { 693 return Misc.getNegativeHighlightColor(); 694 } 695 return Misc.getHighlightColor(); 696 } 697 698 public float getVeryApproximateFPStrength(StarSystemAPI system) { 699 float mag = getTotalActivityMagnitude(system, true); 700 //mag *= getProgressFraction(); 701 mag *= 0.2f + getMarketPresenceFactor(system) * 0.8f; 702 return mag * 1000f; 703 } 704 705 706 /** 707 * From 0 (at one size-3 market) to 1 (maxSize + count >= 7). Also capped based on largest market. 708 * @param system 709 * @return 710 */ 711 public float getMarketPresenceFactor(StarSystemAPI system) { 712 float maxSize = 0; 713 float count = 0; 714 for (MarketAPI market : Misc.getMarketsInLocation(system, Factions.PLAYER)) { 715 maxSize = Math.max(market.getSize(), maxSize); 716 count++; 717 } 718 719 float f = (maxSize - 3f + count - 1f) / 3f; 720 721 float cap = 0.35f; 722 if (maxSize <= 4f) cap = 0.55f; 723 else if (maxSize <= 5f) cap = 0.75f; 724 else cap = 1f; 725 726 if (f < 0f) f = 0f; 727 if (f > cap) f = cap; 728 729 return f; 730 } 731 732 public float getTotalActivityMagnitude(StarSystemAPI system) { 733 return getTotalActivityMagnitude(system, true); 734 } 735 public float getTotalActivityMagnitude(StarSystemAPI system, boolean capped) { 736 //if (true) return 0.1f; 737 float total = 0f; 738 for (EventFactor factor : factors) { 739 if (factor instanceof BaseHostileActivityFactor) { 740 total += ((BaseHostileActivityFactor) factor).getEffectMagnitude(system); 741 } 742 } 743 744 if (capped && total > 1f) total = 1f; 745 746 total = Math.round(total * 100f) / 100f; 747 748 return total; 749 } 750 751 @Override 752 protected void advanceImpl(float amount) { 753 super.advanceImpl(amount); 754 //blowback = 55; 755 if (systemSpawnMults != null) { 756 float days = Misc.getDays(amount); 757 for (MutableStatWithTempMods stat : systemSpawnMults.values()) { 758 stat.advance(days); 759 } 760 } 761 } 762 763 public MutableStatWithTempMods getNumFleetsStat(StarSystemAPI system) { 764 if (system == null) { 765 return new MutableStatWithTempMods(1f); 766 } 767 if (systemSpawnMults == null) { 768 systemSpawnMults = new LinkedHashMap<String, MutableStatWithTempMods>(); 769 } 770 771 String id = system.getId(); 772 MutableStatWithTempMods stat = systemSpawnMults.get(id); 773 if (stat == null) { 774 stat = new MutableStatWithTempMods(1f); 775 systemSpawnMults.put(id, stat); 776 } 777 return stat; 778 } 779 780 public float getNumFleetsMultiplier(StarSystemAPI system) { 781 return getNumFleetsStat(system).getModifiedValue(); 782 } 783 784 public void economyUpdated() { 785 cleanUpHostileActivityConditions(); 786 } 787 788 789 public void cleanUpHostileActivityConditions() { 790 for (MarketAPI curr : Misc.getPlayerMarkets(false)) { 791 if (curr.hasCondition(Conditions.HOSTILE_ACTIVITY)) { 792 curr.removeCondition(Conditions.HOSTILE_ACTIVITY); 793 } 794 } 795 } 796 797 public boolean isEconomyListenerExpired() { 798 return isEnding() || isEnded(); 799 } 800 801 public void commodityUpdated(String commodityId) { 802 } 803 804 805 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 806 } 807 808 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 809 if (isEnded() || isEnding()) return; 810 811 if (!battle.isPlayerInvolved()) return; 812 813 if (Global.getSector().getCurrentLocation() instanceof StarSystemAPI && 814 battle.getPlayerSide().contains(primaryWinner)) { 815 StarSystemAPI system = (StarSystemAPI) Global.getSector().getCurrentLocation(); 816 for (CampaignFleetAPI otherFleet : battle.getNonPlayerSideSnapshot()) { 817 if (otherFleet.isStationMode() && otherFleet.getFleetData().getMembersListCopy().isEmpty()) { 818 { 819 PirateBaseIntel intel = PirateBaseIntel.getIntelFor(system); 820 if (intel != null && Misc.getStationFleet(intel.getMarket()) == otherFleet && 821 HA_CMD.baseInvolved(system, intel)) { 822 int tier = intel.getTier().ordinal(); 823 if (tier < 0) tier = 0; 824 if (tier > 4) tier = 4; 825 int points = -1 * Global.getSettings().getIntFromArray("HA_pirateBase", tier); 826 HAPirateBaseDestroyedFactor factor = new HAPirateBaseDestroyedFactor(points); 827 addFactor(factor); 828 return; 829 } 830 } 831 { 832 LuddicPathBaseIntel intel = LuddicPathBaseIntel.getIntelFor(system); 833 if (intel != null && Misc.getStationFleet(intel.getMarket()) == otherFleet) { 834 float totalInterest = 0f; 835 float activeCells = 0f; 836 for (StarSystemAPI curr : Misc.getPlayerSystems(false)) { 837 totalInterest += StandardLuddicPathActivityCause2.getPatherInterest(curr, 0f, 0f, 1f); 838 activeCells += StandardLuddicPathActivityCause2.getPatherInterest(curr, 0f, 0f, 1f, true); 839 } 840 841 if (totalInterest > 0) { 842 int flat = Global.getSettings().getInt("HA_patherBaseFlat"); 843 int perCell = Global.getSettings().getInt("HA_patherBasePerActiveCell"); 844 int max = Global.getSettings().getInt("HA_patherBaseMax"); 845 846 int points = -1 * Math.min(max, (flat + perCell * (int) Math.round(activeCells))); 847 HAPatherBaseDestroyedFactor factor = new HAPatherBaseDestroyedFactor(points); 848 addFactor(factor); 849 } 850 return; 851 } 852 } 853 } 854 } 855 856 } 857 858 boolean nearAny = false; 859 for (StarSystemAPI system : Misc.getPlayerSystems(false)) { 860 nearAny |= Misc.isNear(primaryWinner, system.getLocation()); 861 if (nearAny) break; 862 } 863 if (!nearAny) return; 864 865 float fpDestroyed = 0; 866 CampaignFleetAPI first = null; 867 for (CampaignFleetAPI otherFleet : battle.getNonPlayerSideSnapshot()) { 868 //if (!Global.getSector().getPlayerFaction().isHostileTo(otherFleet.getFaction())) continue; 869 for (FleetMemberAPI loss : Misc.getSnapshotMembersLost(otherFleet)) { 870 fpDestroyed += loss.getFleetPointCost(); 871 if (first == null) { 872 first = otherFleet; 873 } 874 } 875 } 876 877 int points = computeProgressPoints(fpDestroyed); 878 if (points > 0) { 879 //points = 700; 880 HAShipsDestroyedFactor factor = new HAShipsDestroyedFactor(-1 * points); 881 //sendUpdateIfPlayerHasIntel(factor, false); // addFactor now sends update 882 addFactor(factor); 883 } 884 } 885 886 public static int computeProgressPoints(float fleetPointsDestroyed) { 887 if (fleetPointsDestroyed <= 0) return 0; 888 889 int points = Math.round(fleetPointsDestroyed / FP_PER_POINT); 890 if (points < 1) points = 1; 891 return points; 892 } 893 894 895 @Override 896 protected void notifyStageReached(EventStageData stage) { 897 if (stage.rollData instanceof HAERandomEventData) { 898 HAERandomEventData data = (HAERandomEventData) stage.rollData; 899 boolean fired = data.factor.fireEvent(this, stage); 900 stage.rollData = null; 901 902 if (stage.id == Stage.HA_EVENT) { 903 int resetProgress = getResetProgress(fired); 904 setProgress(resetProgress); 905 } 906 } else if (stage.id == Stage.HA_EVENT && 907 (stage.rollData == null || RANDOM_EVENT_NONE.equals(stage.rollData))) { 908 stage.rollData = null; 909 int resetProgress = getResetProgress(false); 910 setProgress(resetProgress); 911 } 912 } 913 914 protected int getResetProgress(boolean fired) { 915 if (!HABlowbackFactor.ENABLED) { 916 blowback = 0; 917 } 918 int min = RESET_MIN; 919 if (!fired) min = RESET_MAX - 200; 920 921 int resetAdd = random.nextInt(RESET_MAX - min + 1); 922 resetAdd = Math.min(resetAdd, random.nextInt(RESET_MAX - min + 1)); 923 int resetProgress = min + resetAdd; 924 925 int add = Math.min(blowback, (int)((RESET_MAX - resetProgress) * 0.5f)); 926 if (add > 0) { 927 resetProgress += add; 928 blowback -= add; 929 } 930 return resetProgress; 931 } 932 933 public void resetHA_EVENT() { 934 EventStageData stage = getDataFor(Stage.HA_EVENT); 935 //int resetProgress = stage.progressToRollAt - getRandom().nextInt(100); 936 int resetProgress = getResetProgress(false); 937 resetRandomizedStage(stage); 938 setProgress(resetProgress); 939 } 940 941 public void resetHA_EVENTIfFromFactor(HostileActivityFactor factor) { 942 EventStageData stage = getDataFor(Stage.HA_EVENT); 943 if (stage != null && stage.rollData instanceof HAERandomEventData && 944 ((HAERandomEventData)stage.rollData).factor == factor) { 945 resetHA_EVENT(); 946 } 947 } 948 949 @Override 950 public void resetRandomizedStage(EventStageData stage) { 951 if (stage.rollData instanceof HAERandomEventData) { 952 HAERandomEventData data = (HAERandomEventData) stage.rollData; 953 data.isReset = true; 954 data.factor.resetEvent(this, stage); 955 } 956 super.resetRandomizedStage(stage); 957 } 958 959 protected HostileActivityFactor prevMajorEventPick = null; 960 961 @Override 962 public void rollRandomizedStage(EventStageData stage) { 963 if (stage.id == Stage.HA_EVENT || stage.id == Stage.MINOR_EVENT) { 964 float total = 0f; 965 for (EventFactor factor : factors) { 966 if (factor instanceof BaseHostileActivityFactor) { 967 total += factor.getProgress(this); 968 } 969 } 970 if (total < 1f) total = 1f; 971 //System.out.println("Random: " + random.nextLong()); 972 WeightedRandomPicker<HostileActivityFactor> picker = new WeightedRandomPicker<HostileActivityFactor>(random); 973 for (EventFactor factor : factors) { 974 if (factor instanceof BaseHostileActivityFactor) { 975 BaseHostileActivityFactor curr = (BaseHostileActivityFactor) factor; 976 curr.setRandomizedStageSeed(random.nextLong()); // seed will be reused by .rollEvent() 977 float f = curr.getEventFrequency(this, stage); 978 float w = factor.getProgress(this) / total; 979 if (w > 0) { 980 w = 0.1f + 0.9f * w; 981 } 982 picker.add(curr, f * w); 983 } 984 } 985 986 HostileActivityFactor pick = picker.pickAndRemove(); 987 if (stage.id == Stage.HA_EVENT) { 988 if (prevMajorEventPick == pick && !picker.isEmpty()) { 989 pick = picker.pickAndRemove(); 990 } 991 prevMajorEventPick = pick; 992 } 993 994 if (pick == null) return; 995 996 stage.rollData = null; 997 pick.rollEvent(this, stage); 998 } 999 } 1000 1001 @Override 1002 public Set<String> getIntelTags(SectorMapAPI map) { 1003 Set<String> tags = super.getIntelTags(map); 1004 tags.add(Tags.INTEL_COLONIES); 1005 return tags; 1006 } 1007 1008 1009 1010 @Override 1011 public void addFactor(EventFactor factor) { 1012 if (factor.isOneTime()) { 1013 int points = factor.getProgress(this); 1014 if (points < 0) { 1015 int p = Math.round(-1f * points * HABlowbackFactor.FRACTION); 1016 p = Math.min(p, getProgress()); 1017 if (p > 0) { 1018 addBlowback(p); 1019 } 1020 } 1021 } 1022 super.addFactor(factor); 1023 } 1024 1025 @Override 1026 public void reportEconomyMonthEnd() { 1027 super.reportEconomyMonthEnd(); 1028 1029 if (blowback > 0) { 1030 int amt = Math.round(blowback * HABlowbackFactor.PER_MONTH); 1031 1032 float mult = 1f; 1033 for (EventFactor factor : factors) { 1034 if (factor.isOneTime()) continue; 1035 mult *= factor.getAllProgressMult(this); 1036 } 1037 1038 amt = Math.round(amt * mult); 1039 1040 if (amt < 1) amt = 1; 1041 blowback -= amt; 1042 if (blowback < 0) blowback = 0; 1043 } 1044 } 1045 1046 public void addBlowback(int points) { 1047 if (!HABlowbackFactor.ENABLED) return; 1048 blowback += points; 1049 } 1050 public int getBlowback() { 1051 if (!HABlowbackFactor.ENABLED) { 1052 blowback = 0; 1053 } 1054 return blowback; 1055 } 1056 1057 public void setBlowback(int blowback) { 1058 if (!HABlowbackFactor.ENABLED) return; 1059 this.blowback = blowback; 1060 } 1061 1062 protected String getSoundForOtherUpdate(Object param) { 1063 if (param instanceof HAERandomEventData) { 1064 HAERandomEventData data = (HAERandomEventData) param; 1065 if (data.isReset) return null; 1066 if (data.factor == null) return null; 1067 return data.factor.getEventStageSound(data); 1068 } 1069 return null; 1070 } 1071 1072 1073 @Override 1074 public int getMaxMonthlyProgress() { 1075 if (Misc.isEasy()) { 1076 return Global.getSettings().getInt("ha_maxMonthlyProgressEasy"); 1077 } 1078 return Global.getSettings().getInt("ha_maxMonthlyProgress"); 1079 } 1080 1081 public void storyActionConfirmed(Object buttonId, IntelUIAPI ui) { 1082 if (buttonId == BUTTON_ESCALATE) { 1083 ui.recreateIntelUI(); 1084 } 1085 } 1086 1087 public StoryPointActionDelegate getButtonStoryPointActionDelegate(Object buttonId) { 1088 if (buttonId == BUTTON_ESCALATE) { 1089 StoryOptionParams params = new StoryOptionParams(null, 1, "escalateCrisis", 1090 Sounds.STORY_POINT_SPEND_INDUSTRY, 1091 "Escalated colony crisis"); 1092 return new BaseOptionStoryPointActionDelegate(null, params) { 1093 @Override 1094 public void confirm() { 1095 setProgress(ESCALATE_PROGRESS); 1096 } 1097 1098 @Override 1099 public String getTitle() { 1100 return null; 1101 } 1102 1103 @Override 1104 public void createDescription(TooltipMakerAPI info) { 1105 info.setParaInsigniaLarge(); 1106 info.addPara("Take certain actions to precipitate a crisis more quickly. Sets event progress " 1107 + "to %s points.", -10f, Misc.getHighlightColor(), "" + (int) ESCALATE_PROGRESS); 1108 info.addSpacer(20f); 1109 super.createDescription(info); 1110 } 1111 }; 1112 } 1113 return null; 1114 } 1115 1116} 1117 1118 1119 1120 1121 1122 1123 1124