001package com.fs.starfarer.api.impl.campaign.intel.events; 002 003import java.util.List; 004import java.util.Random; 005 006import java.awt.Color; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.campaign.CampaignFleetAPI; 012import com.fs.starfarer.api.campaign.StarSystemAPI; 013import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction; 014import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 015import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin.ListInfoMode; 016import com.fs.starfarer.api.campaign.econ.MarketAPI; 017import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 018import com.fs.starfarer.api.campaign.listeners.PatherCellListener; 019import com.fs.starfarer.api.impl.campaign.ids.Factions; 020import com.fs.starfarer.api.impl.campaign.ids.Sounds; 021import com.fs.starfarer.api.impl.campaign.intel.MessageIntel; 022import com.fs.starfarer.api.impl.campaign.intel.bases.LuddicPathBaseIntel; 023import com.fs.starfarer.api.impl.campaign.intel.bases.LuddicPathBaseManager; 024import com.fs.starfarer.api.impl.campaign.intel.bases.LuddicPathCellsIntel; 025import com.fs.starfarer.api.impl.campaign.intel.events.BaseEventIntel.EventStageData; 026import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.HAERandomEventData; 027import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.Stage; 028import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel; 029import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel.FGIEventListener; 030import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI; 031import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI.GenericRaidParams; 032import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission; 033import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission.FleetStyle; 034import com.fs.starfarer.api.impl.campaign.rulecmd.HA_CMD; 035import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD.BombardType; 036import com.fs.starfarer.api.ui.LabelAPI; 037import com.fs.starfarer.api.ui.TooltipMakerAPI; 038import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 039import com.fs.starfarer.api.util.Misc; 040import com.fs.starfarer.api.util.WeightedRandomPicker; 041 042public class LuddicPathHostileActivityFactor extends BaseHostileActivityFactor implements PatherCellListener, FGIEventListener { 043 044 public static String DEFEATED_PATHER_EXPEDITION = "$defeatedPatherExpedition"; 045 046 public static String ATTACK_KEY = "$PatherAttack_ref"; 047 048 public static boolean isPlayerDefeatedPatherExpedition() { 049 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(DEFEATED_PATHER_EXPEDITION); 050 } 051 public static void setPlayerDefeatedPatherExpedition() { 052 Global.getSector().getPlayerMemoryWithoutUpdate().set(DEFEATED_PATHER_EXPEDITION, true); 053 } 054 055 056 public static class HAPatherCellsEventData { 057 public LuddicPathCellsIntel cells; 058 public MarketAPI market; 059 public float interest; 060 public HAPatherCellsEventData(LuddicPathCellsIntel cells, MarketAPI market) { 061 this.cells = cells; 062 this.market = market; 063 interest = LuddicPathBaseManager.getLuddicPathMarketInterest(market); 064 } 065 } 066 067 public LuddicPathHostileActivityFactor(HostileActivityEventIntel intel) { 068 super(intel); 069 070 Global.getSector().getListenerManager().addListener(this); 071 } 072 073 public String getProgressStr(BaseEventIntel intel) { 074 return ""; 075 } 076 077 public String getDesc(BaseEventIntel intel) { 078 return "Luddic Path"; 079 } 080 081 public String getNameForThreatList(boolean first) { 082 if (first) return "Luddic Path"; 083 return "Luddic Path"; 084 } 085 086 087 public Color getDescColor(BaseEventIntel intel) { 088 if (getProgress(intel) <= 0) { 089 return Misc.getGrayColor(); 090 } 091 return Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(); 092 } 093 094 @Override 095 public Color getNameColorForThreatList() { 096 return Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(); 097 } 098 099 public TooltipCreator getMainRowTooltip(BaseEventIntel intel) { 100 return new BaseFactorTooltip() { 101 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 102 float opad = 10f; 103 104 tooltip.addPara("Advanced technology and artificial intelligence are anathema to the Luddic Path.", 0f); 105 tooltip.addPara("Most of the Pather fleets are small, engaging in reconnaissance and demanding tithes " 106 + "from unwary travellers, but occasionaly a larger raiding force will make an appearance.", opad); 107 108 addAgreementStatus(tooltip, opad); 109 } 110 }; 111 } 112 113 public static void addAgreementStatus(TooltipMakerAPI tooltip, float initPad) { 114 float opad = 10f; 115 Color p = Misc.getPositiveHighlightColor(); 116 Color h = Misc.getHighlightColor(); 117 if (HA_CMD.playerHasPatherAgreement()) { 118 if (!HA_CMD.playerPatherAgreementIsPermanent()) { 119 float days = HA_CMD.getPlayerPatherAgreementDays(); 120 if (days < 1 && days > 0) days = 1; 121 days = Math.round(days); 122 String dStr = "days"; 123 if ((int)days == 1) dStr = "day"; 124 tooltip.addPara("You've %s a signficant amount to the Pathers, and their fleets and " 125 + "ground-based cells should leave your colonies alone for another %s " + dStr + ".", 126 initPad, new Color[] {p, h}, "tithed", "" + (int)days); 127 } else { 128 tooltip.addPara("You've reached an understanding with the Pathers, and their fleets and " 129 + "ground-based cells should leave your colonies alone in the future, " 130 + "barring unexpected events.", 131 initPad, p, "understanding"); 132 } 133 } else { 134 tooltip.addPara("It's possible that you might reach some kind of understanding with the Pathers, " 135 + "provided you find the right people to talk to.", initPad, 136 h, "find the right people"); 137 138// LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(market); 139// tooltip.addPara("If your colony has active Pather cells, destroying the base that's supplying them " 140// + "will also reduce the level of Pather fleet actvity.", opad, h, 141// "destroying the base"); 142 143 } 144 } 145 146 public boolean shouldShow(BaseEventIntel intel) { 147 //return getProgress(intel) > 0 || HA_CMD.playerHasPatherAgreement(); 148 return getProgress(intel) > 0 || (HA_CMD.playerHasPatherAgreement() && !HA_CMD.playerPatherAgreementIsPermanent()); 149 } 150 151 152 153 @Override 154 public int getMaxNumFleets(StarSystemAPI system) { 155 return Global.getSettings().getInt("luddicPathMaxFleets"); 156 } 157 158 public CampaignFleetAPI createFleet(StarSystemAPI system, Random random) { 159 160 float f = 0f; 161 f += getEffectMagnitude(system); 162 163 if (f > 1f) f = 1f; 164 165 float p = Global.getSettings().getFloat("luddicPathSmallFleetProb"); 166 boolean small = random.nextFloat() < p; 167 168 int difficulty = 0; 169 170 if (small) { 171 difficulty = 1 + random.nextInt(2); 172 } else { 173 difficulty = 3; 174 difficulty += (int) Math.round(f * 5f); 175 difficulty += random.nextInt(6); 176 } 177 178 179 FleetCreatorMission m = new FleetCreatorMission(random); 180 m.beginFleet(); 181 182 Vector2f loc = system.getLocation(); 183 String factionId = Factions.LUDDIC_PATH; 184 185 if (small) { 186 m.createStandardFleet(difficulty, factionId, loc); 187 } else { 188 m.createStandardFleet(difficulty, factionId, loc); 189 } 190 191 m.triggerSetPirateFleet(); 192 m.triggerMakeLowRepImpact(); 193 194 if (!small) { 195 //m.triggerFleetPatherNoDefaultTithe(); 196 m.triggerFleetAllowLongPursuit(); 197 } 198 199 CampaignFleetAPI fleet = m.createFleet(); 200 201 return fleet; 202 } 203 204 205 @Override 206 public void notifyFactorRemoved() { 207 Global.getSector().getListenerManager().removeListener(this); 208 } 209 210 public void notifyEventEnding() { 211 notifyFactorRemoved(); 212 } 213 214 public static HAPatherCellsEventData getPatherCellData(EventStageData stage) { 215 if (stage == null) return null; 216 if (stage.rollData instanceof HAERandomEventData) { 217 HAERandomEventData data = (HAERandomEventData) stage.rollData; 218 if (data.custom instanceof HAPatherCellsEventData) { 219 HAPatherCellsEventData attackData = (HAPatherCellsEventData) data.custom; 220 return attackData; 221 } 222 } 223 return null; 224 } 225 226 227 public void addBulletPointForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info, 228 ListInfoMode mode, boolean isUpdate, Color tc, float initPad) { 229 230 HAPatherCellsEventData data = getPatherCellData(stage); 231 Color c = Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(); 232 233 if (data == null) return; 234 235 LabelAPI label = info.addPara("Signs of a Luddic Path attack targeting %s", 236 initPad, tc, tc, data.market.getName()); 237 label.setHighlight("Luddic Path", data.market.getName()); 238 label.setHighlightColors(c, Misc.getBasePlayerColor()); 239 } 240 241 public void addBulletPointForEventReset(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info, 242 ListInfoMode mode, boolean isUpdate, Color tc, float initPad) { 243 info.addPara("Luddic Path attack averted", tc, initPad); 244 } 245 246 public void addStageDescriptionForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info) { 247 HAPatherCellsEventData data = getPatherCellData(stage); 248 if (data == null) return; 249 250 Color c = Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(); 251 252 float small = 0f; 253 float opad = 10f; 254 255 small = 8f; 256 257 LabelAPI label = info.addPara("There are signs of an impending Luddic Path attack targeting %s." 258 + " If the attack is successful, the colony will suffer a catastrophic saturation bombardment.", 259 small, c, data.market.getName()); 260 label.setHighlight(data.market.getName(), "catastrophic saturation bombardment"); 261 label.setHighlightColors(Misc.getBasePlayerColor(), Misc.getNegativeHighlightColor()); 262 263 264 info.addPara("This attack represents a significant resource investment by the Pathers. " 265 + "If it is defeated, Luddic Path cells Sector-wide will be disrupted and Pathers will take " 266 + "less interest in your operations in the future.", 267 opad, Misc.getPositiveHighlightColor(), "disrupted", "less interest"); 268 269 270 stage.beginResetReqList(info, true, "crisis", opad); 271 label = info.addPara("The Luddic Path cells on %s are disrupted", 272 0f, Misc.getBasePlayerColor(), data.market.getName()); 273 label.setHighlight("Luddic Path", data.market.getName()); 274 label.setHighlightColors(c, Misc.getBasePlayerColor()); 275 276 info.addPara("An agreement is reached with the Luddic Path", 277 0f, Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(), "Luddic Path"); 278 stage.endResetReqList(info, false, "crisis", -1, -1); 279 280 addBorder(info, Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor()); 281 } 282 283 284 public String getEventStageIcon(HostileActivityEventIntel intel, EventStageData stage) { 285 return Global.getSector().getFaction(Factions.LUDDIC_PATH).getCrest(); 286 } 287 288 public TooltipCreator getStageTooltipImpl(final HostileActivityEventIntel intel, final EventStageData stage) { 289 if (stage.id == Stage.HA_EVENT) { 290 return getDefaultEventTooltip("Luddic Path attack", intel, stage); 291// return new BaseFactorTooltip() { 292// @Override 293// public void createTooltip(TooltipMakerAPI info, boolean expanded, Object tooltipParam) { 294// float opad = 10f; 295// info.addTitle("Luddic Path attack"); 296// HAPatherCellsEventData data = getPatherCellData(stage); 297// if (data == null) return; 298// 299// info.addPara("A Luddic Path task force will target %s and attempt an obital bombardment.", 300// opad, Misc.getBasePlayerColor(), data.market.getName()); 301// 302// stage.beginResetReqList(info, true, "crisis", opad); 303// Color c = Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(); 304// LabelAPI label = info.addPara("The Luddic Path cells on %s are disrupted", 305// 0f, Misc.getBasePlayerColor(), data.market.getName()); 306// label.setHighlight("Luddic Path", data.market.getName()); 307// label.setHighlightColors(c, Misc.getBasePlayerColor()); 308// stage.endResetReqList(info, true, "crisis", 309// HostileActivityEventIntel.RESET_MIN, HostileActivityEventIntel.RESET_MAX); 310// } 311// }; 312 } 313 return null; 314 } 315 316 317 public float getEventFrequency(HostileActivityEventIntel intel, EventStageData stage) { 318 if (HA_CMD.playerHasPatherAgreement()) return 0f; 319 320 if (stage.id == Stage.HA_EVENT) { 321 if (pickTargetMarket() != null) { 322 return 10f; 323 } 324 } 325 return 0; 326 } 327 328 public MarketAPI pickTargetMarket() { 329 WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>(getRandomizedStageRandom()); 330 for (MarketAPI market : Misc.getPlayerMarkets(false)) { 331 // to put a damper on shenanigans with establishing and abandoning a colony 332 // with an Alpha Core admin to bait an attack 333 if (market.getDaysInExistence() < 180f && !Global.getSettings().isDevMode()) continue; 334 335 LuddicPathCellsIntel cells = LuddicPathCellsIntel.getCellsForMarket(market); 336 if (cells == null || cells.isSleeper()) continue; 337 338 float w = LuddicPathBaseManager.getLuddicPathMarketInterest(market); 339 picker.add(market, w * w); 340 } 341 return picker.pick(); 342 } 343 344 345// public void resetEvent(HostileActivityEventIntel intel, EventStageData stage) { 346// super.resetEvent(intel, stage); 347//// HAERandomEventData data = (HAERandomEventData) stage.rollData; 348//// intel.sendUpdateIfPlayerHasIntel(data, false); 349//// stage.rollData = null; 350// } 351 352 public void rollEvent(HostileActivityEventIntel intel, EventStageData stage) { 353// if (true) return; 354 355 MarketAPI market = pickTargetMarket(); 356 LuddicPathCellsIntel cells = LuddicPathCellsIntel.getCellsForMarket(market); 357 if (market == null || cells == null) return; 358 359 HAERandomEventData data = new HAERandomEventData(this, stage); 360 data.custom = new HAPatherCellsEventData(cells, market); 361 stage.rollData = data; 362 intel.sendUpdateIfPlayerHasIntel(data, false); 363 } 364 365 public boolean fireEvent(HostileActivityEventIntel intel, EventStageData stage) { 366 //if (true) return false; 367 368 HAPatherCellsEventData data = getPatherCellData(stage); 369 if (data == null || data.market == null || data.cells == null || data.cells.isSleeper()) { 370 return false; 371 } 372 373 if (!data.market.isInEconomy()) return false; 374 375 LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(data.market); 376 if (base == null) return false; 377 378 StarSystemAPI system = data.market.getStarSystem(); 379 if (system == null) return false; 380 381 return startRaid(base.getMarket(), data.market, data.interest, system, stage, getRandomizedStageRandom(3)); 382 } 383 384 public void reportCellsDisrupted(LuddicPathCellsIntel cell) { 385// HostileActivityEventIntel intel = HostileActivityEventIntel.get(); 386// if (intel == null) return; 387 EventStageData stage = intel.getDataFor(Stage.HA_EVENT); 388 HAPatherCellsEventData data = getPatherCellData(stage); 389 if (data != null && data.cells == cell && stage.rollData != null) { 390 intel.resetHA_EVENT(); 391 } 392 } 393 394 public static void avertOrAbortAttack() { 395 if (GenericRaidFGI.get(ATTACK_KEY) != null) { 396 GenericRaidFGI.get(ATTACK_KEY).finish(false); 397 } 398 399 HostileActivityEventIntel intel = HostileActivityEventIntel.get(); 400 if (intel == null) return; 401 402 EventStageData stage = intel.getDataFor(Stage.HA_EVENT); 403 HAPatherCellsEventData data = getPatherCellData(stage); 404 if (data != null && stage.rollData != null) { 405 intel.resetHA_EVENT(); 406 } 407 } 408 409 public boolean startRaid(MarketAPI source, MarketAPI target, float interest, StarSystemAPI system, EventStageData stage, Random random) { 410 411 GenericRaidParams params = new GenericRaidParams(new Random(random.nextLong()), true); 412 params.factionId = source.getFactionId(); 413 params.source = source; 414 415 params.prepDays = 14f + random.nextFloat() * 14f; 416 params.payloadDays = 27f + 7f * random.nextFloat(); 417 418 params.raidParams.where = system; 419 params.raidParams.allowedTargets.add(target); 420 params.raidParams.allowNonHostileTargets = true; 421 params.raidParams.setBombardment(BombardType.SATURATION); 422 423 params.style = FleetStyle.STANDARD; 424 425 426 float w = interest; 427 w += Math.max(0f, (target.getSize() - 2)) * 10f; 428 if (w < 0f) w = 0f; 429 if (w > 50f) w = 50f; 430 431 float f = w / 50f; 432 float totalDifficulty = (0.25f + f * 0.75f) * 40f; 433 434 Random r = getRandomizedStageRandom(7); 435 if (r.nextFloat() < 0.33f) { 436 params.style = FleetStyle.QUANTITY; 437 } 438 439 while (totalDifficulty > 0) { 440// float max = Math.min(10f, totalDifficulty * 0.5f); 441// float min = Math.max(2, max - 2); 442// if (max < min) max = min; 443// 444// int diff = Math.round(StarSystemGenerator.getNormalRandom(r, min, max)); 445 int diff = (int) Math.min(10f, totalDifficulty); 446 if (diff < 2) diff = 2; 447 448 params.fleetSizes.add(diff); 449 totalDifficulty -= diff; 450 } 451 452 453 LuddicPathBaseIntel base = LuddicPathBaseIntel.getIntelFor(source); 454 if (base != null) { 455 if (Misc.isHiddenBase(source) && !base.isPlayerVisible()) { 456 base.makeKnown(); 457 base.sendUpdateIfPlayerHasIntel(LuddicPathBaseIntel.DISCOVERED_PARAM, false); 458 } 459 } 460 461 //PatherAttack raid = new PatherAttack(params); 462 params.memoryKey = ATTACK_KEY; 463 GenericRaidFGI raid = new GenericRaidFGI(params); 464 raid.setListener(this); 465 Global.getSector().getIntelManager().addIntel(raid); 466 467 return true; 468 } 469 470 public void reportFGIAborted(FleetGroupIntel intel) { 471 setPlayerDefeatedPatherExpedition(); 472 473 MessageIntel msg = new MessageIntel(); 474 msg.addLine("Luddic Path cells disrupted", Misc.getBasePlayerColor()); 475 msg.setIcon(Global.getSettings().getSpriteName("intel", "sleeper_cells")); 476 msg.setSound(Sounds.REP_GAIN); 477 Global.getSector().getCampaignUI().addMessage(msg, MessageClickAction.COLONY_INFO); 478 479 List<IntelInfoPlugin> cells = Global.getSector().getIntelManager().getIntel(LuddicPathCellsIntel.class); 480 for (IntelInfoPlugin curr : cells) { 481 LuddicPathCellsIntel cell = (LuddicPathCellsIntel) curr; 482 //if (cell.getMarket().isPlayerOwned()) { 483 cell.makeSleeper(Global.getSettings().getFloat("patherCellDisruptionDuration")); 484 //cell.sendUpdateIfPlayerHasIntel(LuddicPathCellsIntel.UPDATE_DISRUPTED, false); 485 ListenerUtil.reportCellDisrupted(cell); 486 //} 487 } 488 489 } 490 491} 492 493 494 495