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.List; 007import java.util.Random; 008 009import java.awt.Color; 010 011import org.lwjgl.util.vector.Vector2f; 012 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.CampaignFleetAPI; 015import com.fs.starfarer.api.campaign.StarSystemAPI; 016import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 017import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin.ListInfoMode; 018import com.fs.starfarer.api.campaign.econ.MarketAPI; 019import com.fs.starfarer.api.impl.campaign.econ.PiracyRespite; 020import com.fs.starfarer.api.impl.campaign.ids.Factions; 021import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel; 022import com.fs.starfarer.api.impl.campaign.intel.events.BaseEventIntel.EventStageData; 023import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.HAERandomEventData; 024import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.Stage; 025import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel; 026import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel.FGIEventListener; 027import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI; 028import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI.GenericRaidParams; 029import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission; 030import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission.FleetStyle; 031import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator; 032import com.fs.starfarer.api.impl.campaign.rulecmd.KantaCMD; 033import com.fs.starfarer.api.ui.TooltipMakerAPI; 034import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 035import com.fs.starfarer.api.util.Misc; 036import com.fs.starfarer.api.util.WeightedRandomPicker; 037 038public class PirateHostileActivityFactor extends BaseHostileActivityFactor implements FGIEventListener { 039 040// public static class HARaidEventData { 041// public SectorEntityToken source; 042// public StarSystemAPI target; 043// } 044 045 public static String RAID_KEY = "$PirateRaid_ref"; 046 public static String SMALL_RAID_KEY = "$SmallPirateRaid_ref"; 047 048 public static final String DEFEATED_LARGE_PIRATE_RAID = "$defeatedLargePirateRaid"; 049 050 public static boolean isDefeatedLargePirateRaid() { 051 //if (true) return true; 052 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(DEFEATED_LARGE_PIRATE_RAID); 053 } 054 public static void setDefeatedLargePirateRaid(boolean value) { 055 Global.getSector().getPlayerMemoryWithoutUpdate().set(DEFEATED_LARGE_PIRATE_RAID, value); 056 } 057 058 059 public PirateHostileActivityFactor(HostileActivityEventIntel intel) { 060 super(intel); 061 } 062 063 public String getProgressStr(BaseEventIntel intel) { 064 return ""; 065 } 066 067 @Override 068 public int getProgress(BaseEventIntel intel) { 069 if (PiracyRespiteScript.get() != null) { 070 return 0; 071 } 072 return super.getProgress(intel); 073 } 074 075 public String getDesc(BaseEventIntel intel) { 076 return "Pirate activity"; 077 } 078 079 public String getNameForThreatList(boolean first) { 080 if (first) return "Pirates"; 081 return "Pirates"; 082 } 083 084 085 public Color getDescColor(BaseEventIntel intel) { 086 if (getProgress(intel) <= 0) { 087 return Misc.getGrayColor(); 088 } 089 return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor(); 090 } 091 092 public TooltipCreator getMainRowTooltip(BaseEventIntel intel) { 093 return new BaseFactorTooltip() { 094 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 095 float opad = 10f; 096 tooltip.addPara("Piracy follows interstellar civilization almost without exception.", 0f); 097 if (KantaCMD.playerHasProtection()) { 098 tooltip.addPara("However, you have %s, which is enough dissuade most pirates from attacking your interests.", 099 opad, Misc.getPositiveHighlightColor(), "Kanta's protection"); 100 } else { 101 if (KantaCMD.playerEverHadProtection()) { 102 tooltip.addPara("You've %s, and it's not the sort of thing you can do over.", 103 opad, Misc.getNegativeHighlightColor(), "lost Kanta's protection"); 104 } else { 105 tooltip.addPara("Having %s, however, should be enough dissuade most pirates from attacking your interests.", 106 opad, Misc.getHighlightColor(), "Kanta's protection"); 107 } 108 } 109 } 110 }; 111 } 112 113 public boolean shouldShow(BaseEventIntel intel) { 114 return getProgress(intel) > 0 || KantaCMD.playerHasProtection(); 115 } 116 117 @Override 118 public Color getNameColorForThreatList() { 119 return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor(); 120 } 121 122 public Color getNameColor(float mag) { 123 if (mag <= 0f) { 124 return Misc.getGrayColor(); 125 } 126 return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor(); 127 } 128 129 @Override 130 public int getMaxNumFleets(StarSystemAPI system) { 131 if (getProgress(intel) <= 0) { 132 return 1; 133 } 134 return super.getMaxNumFleets(system); 135 } 136 137 public CampaignFleetAPI createFleet(StarSystemAPI system, Random random) { 138 139 float f = 0f; 140 f += getEffectMagnitude(system); 141 142 if (f > 1f) f = 1f; 143 144 int difficulty = 0; 145 difficulty += (int) Math.round(f * 7f); 146 147// int size = 0; 148// for (MarketAPI market : Misc.getMarketsInLocation(system, Factions.PLAYER)) { 149// size = Math.max(market.getSize(), size); 150// } 151// int minDiff = Math.max(0, size - 2); 152 153 float mult = 1f; 154 if (getProgress(intel) <= 0) { 155 mult = 0.5f; 156 } 157 158 int minDiff = Math.round(intel.getMarketPresenceFactor(system) * 6f * mult); 159 160 if (difficulty < minDiff) difficulty = minDiff; 161 162 difficulty += random.nextInt(4); 163 164 FleetCreatorMission m = new FleetCreatorMission(random); 165 m.beginFleet(); 166 167 Vector2f loc = system.getLocation(); 168 String factionId = Factions.PIRATES; 169 170 m.createStandardFleet(difficulty, factionId, loc); 171 m.triggerSetPirateFleet(); 172 m.triggerMakeLowRepImpact(); 173 //m.triggerFleetAllowLongPursuit(); 174 175 CampaignFleetAPI fleet = m.createFleet(); 176 177 return fleet; 178 } 179 180 181 182 183 public void addBulletPointForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info, 184 ListInfoMode mode, boolean isUpdate, Color tc, float initPad) { 185 info.addPara("Rumors of pirate raid", tc, initPad); 186// Color c = Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor(); 187// info.addPara("Rumors of pirate raid", initPad, tc, c, "pirate raid"); 188 } 189 190 public void addBulletPointForEventReset(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info, 191 ListInfoMode mode, boolean isUpdate, Color tc, float initPad) { 192 info.addPara("Pirate raid averted", tc, initPad); 193 } 194 195 public void addStageDescriptionForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info) { 196 float small = 0f; 197 float opad = 10f; 198 199 small = 8f; 200// info.addPara("There are rumors that a pirate raid targeting your colonies " 201// + "may be organized in the near future.", opad); 202// 203// info.addPara(BaseIntelPlugin.BULLET + "If the raid is successful, the targeted colonies will suffer from reduced stability.", opad, 204// Misc.getNegativeHighlightColor(), "reduced stability"); 205// 206// info.addPara(BaseIntelPlugin.BULLET + "If the raid is defeated, your colonies will gain " 207// + "increased accessibility for several cycles.", 208// 0f, Misc.getPositiveHighlightColor(), "increased accessibility"); 209 210 info.addPara("There are rumors that a pirate raid targeting your colonies " 211 + "may be organized in the near future. If the raid is successful, the targeted colonies will " 212 + "suffer from reduced stability.", small, 213 Misc.getNegativeHighlightColor(), "reduced stability"); 214 215 if (stage.id == Stage.HA_EVENT) { 216 if (PiracyRespite.NEW_MODE) { 217 info.addPara("If the raid is defeated, your colonies will suffer less shipping disruptions from " 218 + "piracy for the foreseeable future.", 219 opad, Misc.getPositiveHighlightColor(), "less shipping disruptions"); 220 } else { 221 if (PiracyRespiteScript.DURATION < 0) { 222 info.addPara("If the raid is defeated, your colonies will " 223 + "permanently gain increased accessibility.", 224 opad, Misc.getPositiveHighlightColor(), "increased accessibility"); 225 } else { 226 info.addPara("If the raid is defeated, your colonies will gain " 227 + "increased accessibility for several cycles.", 228 opad, Misc.getPositiveHighlightColor(), "increased accessibility"); 229 } 230 } 231 } 232 233 //if (stage.id == Stage.MINOR_EVENT) { 234 stage.addResetReq(info, false, "crisis", -1, -1, opad); 235// } else { 236// stage.beginResetReqList(info, true, "crisis", opad); 237 // want to keep this less prominent, actually, so: just the above 238// info.addPara("An agreement is reached with Kanta, the pirate queen", 239// 0f, Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(), "Luddic Path"); 240// stage.endResetReqList(info, false, "crisis", -1, -1); 241// } 242 243 244 245 addBorder(info, Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor()); 246 247 248// Color c = Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor(); 249// UIComponentAPI rect = info.createRect(c, 2f); 250// info.addCustomDoNotSetPosition(rect).getPosition().inTL(-small, 0).setSize( 251// info.getWidthSoFar() + small * 2f, Math.max(64f, info.getHeightSoFar() + small)); 252 } 253 254 255 public String getEventStageIcon(HostileActivityEventIntel intel, EventStageData stage) { 256 return Global.getSector().getFaction(Factions.PIRATES).getCrest(); 257 } 258 259 public TooltipCreator getStageTooltipImpl(final HostileActivityEventIntel intel, final EventStageData stage) { 260 if (stage.id == Stage.HA_EVENT || stage.id == Stage.MINOR_EVENT) { 261 if (stage.id == Stage.MINOR_EVENT) { 262 return new BaseFactorTooltip() { 263 @Override 264 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 265 float opad = 10f; 266 tooltip.addTitle("Pirate raid"); 267 tooltip.addPara("A pirate raid will be launched against one of your " 268 + "star systems.", opad); 269 } 270 }; 271 } 272 return getDefaultEventTooltip("Pirate raid", intel, stage); 273// return new BaseFactorTooltip() { 274// @Override 275// public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 276// float opad = 10f; 277// tooltip.addTitle("Pirate raid"); 278// tooltip.addPara("A pirate raid will be launched against one of your " 279// + "star systems.", opad); 280//// tooltip.addPara("If the raid is defeated, your colonies will receive " 281//// + "increased accessibility for several cycles.", 282//// opad, Misc.getPositiveHighlightColor(), "increased accessibility"); 283// stage.addResetReq(tooltip, true, "crisis", HostileActivityEventIntel.RESET_MIN, HostileActivityEventIntel.RESET_MAX, opad); 284// } 285// }; 286 } 287 return null; 288 } 289 290 291 public float getEventFrequency(HostileActivityEventIntel intel, EventStageData stage) { 292 if (KantaCMD.playerHasProtection()) return 0f; 293 294 if (PiracyRespiteScript.get() != null) return 0f; 295 296 if (stage.id == Stage.HA_EVENT || stage.id == Stage.MINOR_EVENT) { 297 StarSystemAPI target = findRaidTarget(intel, stage); 298 MarketAPI source = findRaidSource(intel, stage, target); 299 if (target != null && source != null) { 300 return 10f; 301 } 302 } 303 return 0f; 304 } 305 306 307// public void resetEvent(HostileActivityEventIntel intel, EventStageData stage) { 308// super.resetEvent(intel, stage); 309// } 310 311 public void rollEvent(HostileActivityEventIntel intel, EventStageData stage) { 312// if (true) return; 313 HAERandomEventData data = new HAERandomEventData(this, stage); 314 stage.rollData = data; 315 intel.sendUpdateIfPlayerHasIntel(data, false); 316 } 317 318 public boolean fireEvent(HostileActivityEventIntel intel, EventStageData stage) { 319 StarSystemAPI target = findRaidTarget(intel, stage); 320 MarketAPI source = findRaidSource(intel, stage, target); 321 if (source == null || target == null) { 322 return false; 323 } 324 325 stage.rollData = null; 326 return startRaid(source, target, stage, getRandomizedStageRandom(5)); 327 } 328 329 public StarSystemAPI findRaidTarget(HostileActivityEventIntel intel, EventStageData stage) { 330 WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(getRandomizedStageRandom(3)); 331 332 for (StarSystemAPI system : Misc.getPlayerSystems(false)) { 333 float mag = getEffectMagnitude(system); 334 if (mag < 0.1f && stage.id != Stage.MINOR_EVENT) { 335 //if (mag < 0.2f) { 336 continue; 337 } 338 picker.add(system, mag * mag); 339 } 340 341 return picker.pick(); 342 } 343 344 public MarketAPI findRaidSource(HostileActivityEventIntel intel, EventStageData stage, final StarSystemAPI target) { 345 if (target == null) return null; 346 347 List<MarketAPI> list = new ArrayList<MarketAPI>(); 348 float maxDist = Global.getSettings().getFloat("sectorWidth") * 0.5f; 349 350 for (IntelInfoPlugin curr : Global.getSector().getIntelManager().getIntel(PirateBaseIntel.class)) { 351 PirateBaseIntel base = (PirateBaseIntel) curr; 352 if (base.playerHasDealWithBaseCommander()) continue; 353 354 float dist = Misc.getDistance(target.getLocation(), base.getMarket().getLocationInHyperspace()); 355 if (dist > maxDist) continue; 356 357 list.add(base.getMarket()); 358 } 359 360 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 361 if (Factions.PIRATES.equals(market.getFaction().getId())) { 362 for (MarketAPI other : Misc.getMarketsInLocation(market.getContainingLocation())) { 363 if (other == market) continue; 364 if (!other.getFaction().isHostileTo(market.getFaction())) continue; 365 if (other.getSize() <= market.getSize() - 2) continue; 366 367 float dist = Misc.getDistance(market.getPrimaryEntity().getLocation(), other.getPrimaryEntity().getLocation()); 368 if (dist < 8000) continue; 369 370 list.add(market); 371 } 372 } 373 } 374 375 Collections.sort(list, new Comparator<MarketAPI>() { 376 public int compare(MarketAPI m1, MarketAPI m2) { 377 float d1 = Misc.getDistance(target.getLocation(), m1.getLocationInHyperspace()); 378 float d2 = Misc.getDistance(target.getLocation(), m2.getLocationInHyperspace()); 379 return (int) Math.signum(d1 - d2); 380 } 381 }); 382 383 WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>(getRandomizedStageRandom()); 384 for (int i = 0; i < list.size() && i < 4; i++) { 385 MarketAPI market = list.get(i); 386 float dist = Misc.getDistance(target.getLocation(), market.getLocationInHyperspace()); 387 float w = 100000f / (dist * dist); 388 picker.add(market, w); 389 } 390 391 return picker.pick(); 392 } 393 394 public static void avertOrAbortRaid() { 395 if (GenericRaidFGI.get(SMALL_RAID_KEY) != null) { 396 GenericRaidFGI.get(SMALL_RAID_KEY).finish(false); 397 } 398 399 if (GenericRaidFGI.get(RAID_KEY) != null) { 400 GenericRaidFGI.get(RAID_KEY).finish(false); 401 } 402 403 HostileActivityEventIntel intel = HostileActivityEventIntel.get(); 404 if (intel == null) return; 405 406 HAERandomEventData data = intel.getRollDataForEvent(); 407 if (data != null && data.factor instanceof PirateHostileActivityFactor) { 408 intel.resetHA_EVENT(); 409 } 410 } 411 412 413 public boolean startRaid(MarketAPI source, StarSystemAPI target, EventStageData stage, Random random) { 414 GenericRaidParams params = new GenericRaidParams(new Random(random.nextLong()), true); 415 params.factionId = source.getFactionId(); 416 params.source = source; 417 418 params.prepDays = 7f + random.nextFloat() * 14f; 419 params.payloadDays = 27f + 7f * random.nextFloat(); 420 421 params.raidParams.where = target; 422 for (MarketAPI market : Misc.getMarketsInLocation(target)) { 423 if (market.getFaction().isHostileTo(source.getFaction()) || market.getFaction().isPlayerFaction()) { 424 params.raidParams.allowedTargets.add(market); 425 } 426 } 427 if (params.raidParams.allowedTargets.isEmpty()) return false; 428 params.raidParams.allowNonHostileTargets = true; 429 430 params.style = FleetStyle.STANDARD; 431 432 if (stage.id == Stage.MINOR_EVENT) { 433 params.fleetSizes.add(5); 434 params.fleetSizes.add(3); 435 params.memoryKey = SMALL_RAID_KEY; 436 } else { 437 params.memoryKey = RAID_KEY; 438 439 float mag1 = getEffectMagnitude(target); 440 if (mag1 > 1f) mag1 = 1f; 441 float mag2 = intel.getMarketPresenceFactor(target); 442 float totalDifficulty = (0.25f + mag1 * 0.25f + mag2 * 0.5f) * 100f; 443 444 Random r = getRandomizedStageRandom(7); 445 if (r.nextFloat() < 0.33f) { 446 params.style = FleetStyle.QUANTITY; 447 } 448 449 while (totalDifficulty > 0) { 450 float max = Math.min(10f, totalDifficulty * 0.5f); 451 float min = Math.max(2, max - 2); 452 if (max < min) max = min; 453 454 int diff = Math.round(StarSystemGenerator.getNormalRandom(r, min, max)); 455 456 params.fleetSizes.add(diff); 457 totalDifficulty -= diff; 458 } 459 } 460 461 PirateBaseIntel base = PirateBaseIntel.getIntelFor(source); 462 if (base != null) { 463 if (Misc.isHiddenBase(source) && !base.isPlayerVisible()) { 464 base.makeKnown(); 465 base.sendUpdateIfPlayerHasIntel(PirateBaseIntel.DISCOVERED_PARAM, false); 466 } 467 } 468 469 GenericRaidFGI raid = new GenericRaidFGI(params); 470 if (stage.id == Stage.HA_EVENT) { // don't want piracy respite from the minor raid 471 raid.setListener(this); 472 } 473 Global.getSector().getIntelManager().addIntel(raid); 474 475 return true; 476 } 477 478 public void reportFGIAborted(FleetGroupIntel intel) { 479 setDefeatedLargePirateRaid(true); 480 new PiracyRespiteScript(); 481 } 482 483 484 485 public static void main(String[] args) { 486 Random r = new Random(); 487 int [] counts = new int[11]; 488 for (int i = 0; i < 10000; i++) { 489 int x = Math.round(getNormalRandom(r, 7, 10)); 490 counts[x]++; 491 } 492 for (int i = 0; i < counts.length; i++) { 493 System.out.println(i + ": " + counts[i]); 494 } 495 } 496 497 public static float getNormalRandom(Random random, float min, float max) { 498 double r = random.nextGaussian(); 499 r *= 0.2f; 500 r += 0.5f; 501 if (r < 0) r = 0; 502 if (r > 1) r = 1; 503 504 // 70% chance 0.3 < r < .7 505 // 95% chance 0.1 < r < .7 506 // 99% chance 0 < r < 1 507 return min + (float) r * (max - min); 508 } 509 510} 511 512 513 514