001package com.fs.starfarer.api.impl.campaign.intel.bases; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.Iterator; 007import java.util.List; 008import java.util.Random; 009import java.util.Set; 010 011import java.awt.Color; 012 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.BattleAPI; 015import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 016import com.fs.starfarer.api.campaign.CampaignFleetAPI; 017import com.fs.starfarer.api.campaign.FactionAPI; 018import com.fs.starfarer.api.campaign.SectorEntityToken; 019import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 020import com.fs.starfarer.api.campaign.econ.Industry; 021import com.fs.starfarer.api.campaign.econ.MarketAPI; 022import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 023import com.fs.starfarer.api.impl.campaign.DebugFlags; 024import com.fs.starfarer.api.impl.campaign.econ.RecentUnrest; 025import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI; 026import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.EconomyRouteData; 027import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetRouteManager; 028import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator; 029import com.fs.starfarer.api.impl.campaign.fleets.RouteManager; 030import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData; 031import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 032import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner; 033import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 034import com.fs.starfarer.api.impl.campaign.ids.Conditions; 035import com.fs.starfarer.api.impl.campaign.ids.Factions; 036import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 037import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 038import com.fs.starfarer.api.impl.campaign.ids.Tags; 039import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 040import com.fs.starfarer.api.impl.campaign.intel.events.LuddicPathHostileActivityFactor; 041import com.fs.starfarer.api.impl.campaign.rulecmd.HA_CMD; 042import com.fs.starfarer.api.ui.Alignment; 043import com.fs.starfarer.api.ui.SectorMapAPI; 044import com.fs.starfarer.api.ui.TooltipMakerAPI; 045import com.fs.starfarer.api.util.IntervalUtil; 046import com.fs.starfarer.api.util.Misc; 047import com.fs.starfarer.api.util.WeightedRandomPicker; 048 049public class LuddicPathCellsIntel extends BaseIntelPlugin implements RouteFleetSpawner, FleetEventListener { 050 051 public static String USED_PLANETBUSTER_KEY = "$core_lpUsedPlanetbuster"; 052 053 public static float INCIDENT_PROB = Global.getSettings().getFloat("luddicPathCellsIncidentProbabilityPerMonth"); 054 public static float MIN_WARNING_DAYS = Global.getSettings().getFloat("luddicPathCellsIncidentWarningMinDays"); 055 056// public static float DISRUPTION_MIN = 90; 057// public static float DISRUPTION_RANGE = 60; 058 059 public static float MIN_SABOTAGE = Global.getSettings().getFloatFromArray("luddicPathSabotageDays", 0); 060 public static float MAX_SABOTAGE = Global.getSettings().getFloatFromArray("luddicPathSabotageDays", 1); 061 062 063 public static Object UPDATE_DISSOLVED = new Object(); 064 public static Object UPDATE_DISRUPTED = new Object(); 065 066 public static Object INCIDENT_PREP = new Object(); 067 public static Object INCIDENT_PREVENTED = new Object(); 068 public static Object INCIDENT_HAPPENED = new Object(); 069 070 071 public static enum IncidentType { 072 REDUCED_STABILITY, 073 INDUSTRY_SABOTAGE, 074 PLANETBUSTER, 075 } 076 077 protected boolean sleeper = false; 078 protected float sleeperTimeout = 0f; 079 080 protected MarketAPI market; 081 082 protected IntervalUtil incidentTracker = new IntervalUtil(20f, 40f); 083 protected Random random = new Random(); 084 085 protected int numIncidentAttempts = 0; 086 087 protected float incidentDelay = 0f; 088 protected IncidentType incidentType = null; 089 protected RouteData smuggler = null; 090 091 protected IncidentType prevIncident = null; 092 protected boolean prevIncidentSucceeded = false; 093 protected float sincePrevIncident = 0f; 094 protected Object prevIncidentData = null; 095 096 protected float inertiaTime = 0f; // time the cell has existed despite not being a priority for the LP to maintain 097 098 099 public LuddicPathCellsIntel(MarketAPI market, boolean sleeper) { 100 this.market = market; 101 this.sleeper = sleeper; 102 103 if (!market.isPlayerOwned()) { 104 setPostingLocation(market.getPrimaryEntity()); 105 } 106 107 if (!market.hasCondition(Conditions.PATHER_CELLS)) { 108 market.addCondition(Conditions.PATHER_CELLS, this); 109 } 110 111 Global.getSector().addScript(this); 112 113 if (market.isPlayerOwned() || DebugFlags.PATHER_BASE_DEBUG) { 114 Global.getSector().getIntelManager().addIntel(this); 115 } else { 116 Global.getSector().getIntelManager().queueIntel(this); 117 } 118 } 119 120 public static LuddicPathBaseIntel getClosestBase(MarketAPI market) { 121 List<IntelInfoPlugin> bases = Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class); 122 float minDist = Float.MAX_VALUE; 123 LuddicPathBaseIntel closest = null; 124 for (IntelInfoPlugin curr : bases) { 125 LuddicPathBaseIntel intel = (LuddicPathBaseIntel) curr; 126 float dist = Misc.getDistance(intel.getMarket().getLocationInHyperspace(), market.getLocationInHyperspace()); 127 if (dist < minDist) { 128 minDist = dist; 129 closest = intel; 130 } 131 } 132 return closest; 133 } 134 135 public static List<LuddicPathCellsIntel> getCellsForBase(LuddicPathBaseIntel base, boolean includeSleeper) { 136 List<LuddicPathCellsIntel> result = new ArrayList<LuddicPathCellsIntel>(); 137 138 List<IntelInfoPlugin> cells = Global.getSector().getIntelManager().getIntel(LuddicPathCellsIntel.class); 139 for (IntelInfoPlugin curr : cells) { 140 LuddicPathCellsIntel intel = (LuddicPathCellsIntel) curr; 141 if (!includeSleeper && intel.isSleeper()) continue; 142 if (getClosestBase(intel.getMarket()) == base) { 143 result.add(intel); 144 } 145 } 146 return result; 147 } 148 149 public static LuddicPathCellsIntel getCellsForMarket(MarketAPI market) { 150 if (market == null) return null; 151 List<IntelInfoPlugin> cells = Global.getSector().getIntelManager().getIntel(LuddicPathCellsIntel.class); 152 for (IntelInfoPlugin curr : cells) { 153 LuddicPathCellsIntel intel = (LuddicPathCellsIntel) curr; 154 if (intel.getMarket() == market) return intel; 155 } 156 return null; 157 } 158 159 160 public MarketAPI getMarket() { 161 return market; 162 } 163 164 @Override 165 public boolean canMakeVisibleToPlayer(boolean playerInRelayRange) { 166 return super.canMakeVisibleToPlayer(playerInRelayRange); 167 } 168 169 @Override 170 protected void notifyEnded() { 171 super.notifyEnded(); 172 Global.getSector().removeScript(this); 173 } 174 175 176 @Override 177 protected void notifyEnding() { 178 super.notifyEnding(); 179 180 if (market.hasCondition(Conditions.PATHER_CELLS)) { 181 market.removeCondition(Conditions.PATHER_CELLS); 182 } 183 } 184 185 186 public void makeSleeper() { 187 makeSleeper(-1); 188 } 189 public void makeSleeper(float sleeperTimeout) { 190 if (sleeperTimeout >= 0) { 191 this.sleeperTimeout = sleeperTimeout; 192 } 193 sleeper = true; 194 } 195 public void makeActiveIfPossible() { 196 if (sleeperTimeout <= 0) { 197 sleeper = false; 198 } 199 } 200 201 202 @Override 203 protected void advanceImpl(float amount) { 204 super.advanceImpl(amount); 205 206 float days = Misc.getDays(amount); 207 208 inertiaTime += days; 209 210 if (sleeperTimeout > 0) { 211 sleeperTimeout -= days; 212 if (sleeperTimeout < 0) { 213 sleeperTimeout = 0; 214 } 215 } 216 217 if (!market.isInEconomy()) { 218 endImmediately(); 219 return; 220 } 221 222 if (isSleeper()) return; 223 224 // incidents handled through HostileActivityEventIntel now 225 // not anymore since the change from Hostile Activity -> Colony Crises 226 //if (market.isPlayerOwned()) return; 227 228 if (DebugFlags.PATHER_BASE_DEBUG) { 229 days *= 200f; 230 } 231 232 if (prevIncident != null) { 233 float mult = 1f; 234 if (DebugFlags.PATHER_BASE_DEBUG) { 235 mult = 1f / 20f; 236 } 237 sincePrevIncident += days * mult; 238 if (sincePrevIncident >= 180f) { 239 sincePrevIncident = 0f; 240 prevIncident = null; 241 prevIncidentData = null; 242 prevIncidentSucceeded = false; 243 } 244 } 245// if (market.isPlayerOwned()) { 246// System.out.println("wefwefwe"); 247// } 248 if (incidentType == null && prevIncident == null) { 249 incidentTracker.advance(days); 250 if (incidentTracker.intervalElapsed() && random.nextFloat() < INCIDENT_PROB) { 251 prepareIncident(); 252 numIncidentAttempts++; 253 } 254 } else if (incidentType != null) { 255 if (incidentDelay > 0 && smuggler == null) { 256 incidentDelay -= days; 257 } 258 259 if (smuggler == null && incidentDelay <= 0) { 260 beginIncident(); 261 } 262// if(market.isPlayerOwned()) { 263// System.out.println("efwwefew"); 264// } 265// smuggler.getActiveFleet().getLocation() 266// smuggler.getActiveFleet().getContainingLocation() 267 if (smuggler != null) { 268 RouteSegment segment = smuggler.getCurrent(); 269 if (segment != null && segment.getId() == EconomyFleetRouteManager.ROUTE_TRAVEL_SRC) { 270// if(market.isPlayerOwned()) { 271// System.out.println("efwwefew"); 272// } 273 doIncident(); 274 } 275 } 276 } 277 } 278 279 280 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 281 282 Color h = Misc.getHighlightColor(); 283 Color g = Misc.getGrayColor(); 284 float pad = 3f; 285 float opad = 10f; 286 287 float initPad = pad; 288 if (mode == ListInfoMode.IN_DESC) initPad = opad; 289 290 Color tc = getBulletColorForMode(mode); 291 292 bullet(info); 293 boolean isUpdate = getListInfoParam() != null; 294 295 if (mode != ListInfoMode.IN_DESC) { 296 addMarketToList(info, market, initPad, tc); 297 initPad = 0f; 298 } 299 //info.addPara(market., initPad) 300 301 if (isUpdate) { 302 if (getListInfoParam() == INCIDENT_HAPPENED) { 303 if (!prevIncidentSucceeded) { 304 info.addPara("Incident averted by local security forces", initPad); 305 } else { 306 switch (prevIncident) { 307 case INDUSTRY_SABOTAGE: 308 Industry ind = (Industry) prevIncidentData; 309 String days = getDays(ind.getDisruptedDays()); 310 String daysStr = getDaysString(ind.getDisruptedDays()); 311 info.addPara(ind.getCurrentName() + " operations disrupted for %s " + daysStr, initPad, tc, h, days); 312 break; 313 case REDUCED_STABILITY: 314 info.addPara("Stability reduced by %s", initPad, tc, h, "" + (Integer) prevIncidentData); 315 break; 316 case PLANETBUSTER: 317 info.addPara("Colony destroyed by planetbuster", initPad, tc); 318 break; 319 } 320 } 321 } 322 } 323 324 unindent(info); 325 } 326 327 @Override 328 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 329 Color c = getTitleColor(mode); 330 info.addPara(getName(), c, 0f); 331 addBulletPoints(info, mode); 332 } 333 334 public void addInterestInfo(TooltipMakerAPI info, float width, float height) { 335 Color h = Misc.getHighlightColor(); 336 float opad = 10f; 337 338 info.addSectionHeading("Pather interest", getFactionForUIColors().getBaseUIColor(), 339 getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad); 340 341 info.addPara("The following activity is attracting Pather interest, whether due to AI core use or the inherent nature of the industry:", opad); 342 343 List<Industry> industries = new ArrayList<Industry>(market.getIndustries()); 344 Iterator<Industry> iter = industries.iterator(); 345 while (iter.hasNext()) { 346 if (iter.next().isHidden()) { 347 iter.remove(); 348 } 349 } 350 Collections.sort(industries, new Comparator<Industry>() { 351 public int compare(Industry o1, Industry o2) { 352 float s1 = o1.getPatherInterest(); 353 float s2 = o2.getPatherInterest(); 354 return (int) Math.signum(s2 - s1); 355 } 356 }); 357 String indent = " "; 358 float initPad = 5f; 359 boolean added = false; 360 361 String aiCoreId = market.getAdmin().getAICoreId(); 362 if (aiCoreId != null) { 363 int s = (int) Math.round(LuddicPathBaseManager.AI_CORE_ADMIN_INTEREST); 364 if (market.getAdmin().getMemoryWithoutUpdate().getBoolean(MemFlags.SUSPECTED_AI)) { 365 info.addPara(indent + "Suspected AI core administrator (%s)", initPad, h, "" + s); 366 } else { 367 info.addPara(indent + "AI core administrator (%s)", initPad, h, "" + s); 368 } 369 initPad = 3f; 370 added = true; 371 } 372 373 for (Industry ind : industries) { 374 //float score = LuddicPathBaseManager.getLuddicPathMarketInterest(market); 375 float score = ind.getPatherInterest(); 376 if ((int)Math.round(score) != 0) { 377 int s = (int) Math.round(score); 378 info.addPara(indent + ind.getCurrentName() + " (%s)", initPad, h, "" + s); 379 initPad = 3f; 380 added = true; 381 } 382 } 383 384 385 386 if (!added) { 387 info.addPara(indent + "None", initPad); 388 } 389 390 if (market.isPlayerOwned() && LuddicPathHostileActivityFactor.isPlayerDefeatedPatherExpedition()) { 391 info.addPara("You defeated a Luddic Path armada sent against you, and " 392 + "they are now more reluctant to take active measures against your colonies.", opad); 393 } 394 395 } 396 397 398 @Override 399 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 400 Color h = Misc.getHighlightColor(); 401 Color g = Misc.getGrayColor(); 402 Color tc = Misc.getTextColor(); 403 float pad = 3f; 404 float opad = 10f; 405 406 if (width > 0) { // it's 0 when called from market condition tooltip 407 info.addImage(getFactionForUIColors().getLogo(), width, 128, opad); 408 } 409 410 if (isEnding()) { 411 info.addPara("The Pather cells " + 412 market.getOnOrAt() + " " + market.getName() + " have been dissolved.", opad); 413 } else if (isSleeper() && sleeperTimeout <= 0) { 414 info.addPara("There are indications that sleeper Luddic Path cells are being organized " + 415 market.getOnOrAt() + " " + market.getName() + ".", opad); 416 info.addPara("The Pathers have not made any significant moves, but are apparently preparing " + 417 "to do so if whatever activity they object to - industrial development, or the suspected " + 418 "use of AI cores, and other such - continues.", opad); 419 } else { 420 info.addPara("There are active Luddic Path cells " + 421 market.getOnOrAt() + " " + market.getName() + ".", opad); 422// info.addPara("They are engaged in planning acts of terror and industrial sabotage, but " + 423// "need material support - smuggled in from the nearest Pather base - to carry them off." 424// + " The cells also provide intel to Pather fleets operating in-system.", opad); 425 info.addPara("They are engaged in planning acts of terror and industrial sabotage, but " + 426 "are unlikely to carry them off unless the overal level of hostile activity in the system " 427 + "provides sufficient cover." 428 + " The cells also provide intel to Pather fleets operating in-system.", opad); 429 430 if (sleeperTimeout > 0) { 431 int daysNum = (int) Math.round(sleeperTimeout); 432 if (daysNum < 1) daysNum = 1; 433 String days = getDaysString(daysNum); 434 info.addPara("However, the base supporting these cells is no longer operational. " + 435 "It is projected that establishing a new support network will take at least " + 436 "%s " + days + ", provided another base exists.", opad, 437 h, 438 "" + daysNum); 439 } else { 440 LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(market); 441 if (base != null) { 442 if (base.isPlayerVisible()) { 443 info.addPara("The Pather base at the " + base.getMarket().getStarSystem().getNameWithLowercaseType() + 444 " is providing support to these cells.", opad); 445 } else { 446 info.addPara("You do not know the location of the Pather base providing support to these cells.", opad); 447 } 448 449 info.addPara("If the base is destroyed, it will take some time to organize " + 450 "support from another base, and both ground and fleet operations will be disrupted.", opad); 451 } 452 } 453 } 454 455 if (!isEnding()) { 456 info.addSectionHeading("Impact", getFactionForUIColors().getBaseUIColor(), 457 getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad); 458 459 if (!isSleeper()) { 460 float stability = LuddicPathCells.STABLITY_PENALTY; 461// info.addPara("%s stability. Possibility of various acts of terror and sabotage, " + 462// "if smugglers from a Luddic Path base are able to provide material support.", 463// opad, h, 464// "-" + (int)stability); 465 info.addPara("%s stability.", 466 opad, h, 467 "-" + (int)stability); 468 } else { 469 if (sleeperTimeout <= 0) { // only show for actual sleeper cells, not "disrupted" active cells 470 info.addPara("No perceptible impact on operations as of yet.", opad); 471 } else { 472 info.addPara("No impact on operations due to lack of material support.", opad); 473 } 474 } 475 476 //addInterestInfo(info, width, height); 477 } 478 479 if (prevIncident != null || incidentType == IncidentType.PLANETBUSTER) { 480 info.addSectionHeading("Recent events", getFactionForUIColors().getBaseUIColor(), 481 getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad); 482 483 if (prevIncidentSucceeded) { 484 if (incidentType == IncidentType.PLANETBUSTER) { 485 info.addPara("There are indications that the Pather cells are preparing to sneak " + 486 "a planetbuster onto " + market.getName() + ". " + 487 "If they succeed, the colony will effectively be destroyed.", opad); 488 } else if (prevIncident != null) { 489 switch (prevIncident) { 490 case INDUSTRY_SABOTAGE: 491 if (prevIncidentData instanceof Industry) { 492 Industry ind = (Industry) prevIncidentData; 493 if (ind.getDisruptedDays() > 2) { 494 String days = getDays(ind.getDisruptedDays()); 495 String daysStr = getDaysString(ind.getDisruptedDays()); 496 info.addPara("The Pather cells have conducted a successful act of sabotage, " + 497 "disrupting " + ind.getCurrentName() + " operations for %s " + daysStr + ".", 498 opad, h, days); 499 } 500 } 501 break; 502 case REDUCED_STABILITY: 503 info.addPara("The Pather cells have conducted low-level attacks on various " + 504 "industrial, military, and civilian targets, reducing stability by %s.", 505 opad, h, "" + (Integer) prevIncidentData); 506 break; 507 case PLANETBUSTER: 508 info.addPara("The Pather cells have smuggled a planetbuster onto " + market.getName() + 509 " and detonated it. The colony has been effectively destroyed.", opad); 510 break; 511 } 512 } 513 } else { 514 if (prevIncident != null) { 515 switch (prevIncident) { 516 case INDUSTRY_SABOTAGE: 517 if (prevIncidentData instanceof Industry) { 518 Industry ind = (Industry) prevIncidentData; 519 info.addPara("An attempted act of sabotage against " + 520 ind.getCurrentName() + " operations was averted by the local security forces.", 521 opad); 522 } 523 break; 524 case REDUCED_STABILITY: 525 info.addPara("Multiple planned attacks against various industrial, " + 526 "military, and civilian targets " + 527 " were averted by the local security forces.", 528 opad); 529 break; 530 case PLANETBUSTER: 531 info.addPara("The Pather cells have smuggled a planetbuster onto " + market.getName() + 532 ", but the local security forces were able to locate and disarm it, thereby " + 533 "saving the colony.", opad); 534 break; 535 } 536 } 537 } 538 539 addBulletPoints(info, ListInfoMode.IN_DESC); 540 } 541 542 if (!isEnding()) { 543 addInterestInfo(info, width, height); 544 } 545 } 546 547 public List<ArrowData> getArrowData(SectorMapAPI map) { 548 if (sleeperTimeout > 0) return null; 549 550 LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(market); 551 if (base == null || !base.isPlayerVisible()) return null; 552 553 List<ArrowData> result = new ArrayList<ArrowData>(); 554 555 556 SectorEntityToken entityFrom = base.getMapLocation(map); 557 if (map != null) { 558 SectorEntityToken iconEntity = map.getIntelIconEntity(base); 559 if (iconEntity != null) { 560 entityFrom = iconEntity; 561 } 562 } 563 564 ArrowData arrow = new ArrowData(entityFrom, market.getPrimaryEntity()); 565 arrow.color = getFactionForUIColors().getBaseUIColor(); 566 result.add(arrow); 567 568 return result; 569 } 570 571 @Override 572 public String getIcon() { 573 if (isSleeper()) { 574 return Global.getSettings().getSpriteName("intel", "sleeper_cells"); 575 } 576 return Global.getSettings().getSpriteName("intel", "active_cells"); 577 } 578 579 @Override 580 public Set<String> getIntelTags(SectorMapAPI map) { 581 Set<String> tags = super.getIntelTags(map); 582 tags.add(Factions.LUDDIC_PATH); 583 584 if (market.isPlayerOwned() && !isSleeper()) { 585 tags.add(Tags.INTEL_COLONIES); 586 } 587 588 return tags; 589 } 590 591 public String getSortString() { 592 String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix()); 593 if (sleeper) { 594 return base + " D"; // so it goes after "Luddic Path Base" 595 } 596 return base + " C"; // so it goes after "Luddic Path Base" 597 } 598 599 public String getName() { 600 String base = "Luddic Path Cells"; 601 602 if (isSendingUpdate()) { 603 if (getListInfoParam() == INCIDENT_HAPPENED) { 604 if (prevIncidentSucceeded) { 605 return base + " - Incident"; 606 } else { 607 return base + " - Incident Averted"; 608 } 609 } 610 } 611 612 if (isEnding()) { 613 return base + " - Dissolved"; 614 } 615 if (sleeperTimeout > 0) { 616 return base + " - Disrupted"; 617 } 618 if (isSleeper()) { 619 return base + " - Sleeper"; 620 } else { 621 return base + " - Active"; 622 } 623 } 624 625 @Override 626 public FactionAPI getFactionForUIColors() { 627 return Global.getSector().getFaction(Factions.LUDDIC_PATH); 628 } 629 630 public String getSmallDescriptionTitle() { 631 return getName(); 632 } 633 634 @Override 635 public SectorEntityToken getMapLocation(SectorMapAPI map) { 636 return market.getPrimaryEntity(); 637 } 638 639 640 @Override 641 public String getCommMessageSound() { 642 return super.getCommMessageSound(); 643 } 644 645 public boolean isSleeper() { 646 if (Factions.PLAYER.equals(market.getFactionId())) { 647 if (HA_CMD.playerHasPatherAgreement()) { 648 return true; 649 } 650 } 651 return sleeper; 652 } 653 654 public void setSleeper(boolean sleeper) { 655 this.sleeper = sleeper; 656 } 657 658 public float getSleeperTimeout() { 659 return sleeperTimeout; 660 } 661 662 public void setSleeperTimeout(float sleeperTimeout) { 663 this.sleeperTimeout = sleeperTimeout; 664 } 665 666 667 public String getRouteSourceId() { 668 return EconomyFleetRouteManager.SOURCE_ID; 669 //return "pather_cells_smuggler"; 670 } 671 672 673 public void prepareIncident() { 674 abortIncident(); 675 //if (incidentType != null) return; 676 LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(getMarket()); 677 if (base == null) return; 678 679 WeightedRandomPicker<IncidentType> types = new WeightedRandomPicker<IncidentType>(random); 680 681 types.add(IncidentType.REDUCED_STABILITY, 10f); 682 if (numIncidentAttempts >= 3 || !market.isPlayerOwned()) { 683 types.add(IncidentType.INDUSTRY_SABOTAGE, 10f); 684 } 685 686// if (numIncidentAttempts >= 10 && market.getSize() >= 5 && 687// !Global.getSector().getMemory().is(USED_PLANETBUSTER_KEY, true)) { 688// types.add(IncidentType.PLANETBUSTER, 5f); 689// } 690 691 incidentType = types.pick(); 692 //incidentType = IncidentType.REDUCED_STABILITY; 693 incidentDelay = MIN_WARNING_DAYS + random.nextFloat() * MIN_WARNING_DAYS; 694 695 696 if (incidentType == IncidentType.PLANETBUSTER) { 697 incidentDelay = MIN_WARNING_DAYS * 4f + random.nextFloat() * 30f; 698 Global.getSector().getMemoryWithoutUpdate().set(USED_PLANETBUSTER_KEY, true, 1500f); 699 } 700 701// if (market.isPlayerOwned()) { 702// sendUpdateIfPlayerHasIntel(INCIDENT_PREP, false); 703// } 704 } 705 706 public void beginIncident() { 707 LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(getMarket()); 708 if (base == null) { 709 abortIncident(); 710 return; 711 } 712 713 sendSmuggler(base); 714 } 715 716 public void abortIncident() { 717 incidentDelay = 0; 718 incidentType = null; 719 if (smuggler != null && smuggler.getActiveFleet() != null) { 720 smuggler.getActiveFleet().removeEventListener(this); 721 } 722 smuggler = null; 723 } 724 725 protected boolean checkSuccess() { 726 float pSuccess = 1f - market.getStabilityValue() * 0.075f; 727 return random.nextFloat() < pSuccess; 728 } 729 730 public void doIncident() { 731 if (incidentType == null) return; 732 733 prevIncidentData = null; 734 735 boolean success = checkSuccess(); 736 737 if (incidentType == IncidentType.REDUCED_STABILITY) { 738 if (success) { 739 RecentUnrest.get(market).add(3, 740 Misc.ucFirst(Global.getSector().getFaction(Factions.LUDDIC_PATH).getPersonNamePrefix()) + " sabotage"); 741 prevIncidentData = 3; 742 } 743 } else if (incidentType == IncidentType.INDUSTRY_SABOTAGE) { 744 WeightedRandomPicker<Industry> picker = new WeightedRandomPicker<Industry>(random); 745 for (Industry ind : market.getIndustries()) { 746 if (!ind.canBeDisrupted()) continue; 747 picker.add(ind, ind.getPatherInterest()); 748 } 749 Industry target = picker.pick(); 750 if (target == null) { 751 abortIncident(); 752 return; 753 } 754 755 prevIncidentData = target; 756 if (success) { 757 float disruptionDur = MIN_SABOTAGE + random.nextFloat() * (MAX_SABOTAGE - MIN_SABOTAGE); 758 target.setDisrupted(disruptionDur, true); 759 } 760 } else if (incidentType == IncidentType.PLANETBUSTER) { 761 // ??? turn into lava planet, remove market, etc 762 } 763 764 prevIncident = incidentType; 765 sincePrevIncident = 0f; 766 prevIncidentSucceeded = success; 767 768 769 if (DebugFlags.SEND_UPDATES_WHEN_NO_COMM || Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay() 770 || market.isPlayerOwned()) { 771 if (market.isPlayerOwned() || 772 incidentType == IncidentType.INDUSTRY_SABOTAGE || 773 incidentType == IncidentType.PLANETBUSTER) { 774 sendUpdateIfPlayerHasIntel(INCIDENT_HAPPENED, false); 775 } 776 } 777 778 abortIncident(); 779 } 780 781 782 protected void sendSmuggler(LuddicPathBaseIntel base) { 783 String sid = getRouteSourceId(); 784 785 SectorEntityToken from = base.getMarket().getPrimaryEntity(); 786 SectorEntityToken to = getMarket().getPrimaryEntity(); 787 788 EconomyRouteData data = new EconomyRouteData(); 789 data.from = base.getMarket(); 790 data.to = market; 791 data.smuggling = true; 792 data.cargoCap = 400; 793 data.fuelCap = 200; 794 795 OptionalFleetData extra = new OptionalFleetData(data.from); 796 extra.fleetType = FleetTypes.TRADE_SMUGGLER; 797 798 RouteData route = RouteManager.getInstance().addRoute(sid, base.getMarket(), Misc.genRandomSeed(), extra, this, data); 799 extra.strength = 50f; 800 extra.strength = Misc.getAdjustedStrength(extra.strength, base.getMarket()); 801 802 803 float orbitDays = 3f + random.nextFloat() * 3f; 804 float travelDays = RouteLocationCalculator.getTravelDays(from, to); 805 if (DebugFlags.PATHER_BASE_DEBUG) travelDays *= 0.1f; 806 807 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_SRC_LOAD, orbitDays, from)); 808 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_TRAVEL_DST, travelDays, from, to)); 809 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_DST_UNLOAD, orbitDays * 0.5f, to)); 810 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_DST_LOAD, orbitDays * 0.5f, to)); 811 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_TRAVEL_SRC, travelDays, to, from)); 812 route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_SRC_UNLOAD, orbitDays, from)); 813 814 smuggler = route; 815 } 816 817 public void reportAboutToBeDespawnedByRouteManager(RouteData route) { 818 819 } 820 821 public boolean shouldCancelRouteAfterDelayCheck(RouteData route) { 822 return false; 823 } 824 825 public boolean shouldRepeat(RouteData route) { 826 return false; 827 } 828 829 public CampaignFleetAPI spawnFleet(RouteData route) { 830 Random random = new Random(); 831 if (route.getSeed() != null) { 832 random = new Random(route.getSeed()); 833 } 834 835 CampaignFleetAPI fleet = EconomyFleetRouteManager.createTradeRouteFleet(route, random); 836 if (fleet == null) return null;; 837 838 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_DO_NOT_IGNORE_PLAYER, true); 839 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_IGNORES_OTHER_FLEETS, true); 840 fleet.addEventListener(this); 841 fleet.addScript(new EconomyFleetAssignmentAI(fleet, route)); 842 return fleet; 843 } 844 845 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 846 if (smuggler == null || smuggler.getActiveFleet() == null) return; 847 848 CampaignFleetAPI active = smuggler.getActiveFleet(); 849 if (!battle.isInvolved(active)) return; 850 851 if (battle.getSideFor(active) != battle.getSideFor(primaryWinner)) { 852 abortIncident(); 853 } 854 } 855 856 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 857 if (smuggler != null && fleet == smuggler.getActiveFleet()) { 858 abortIncident(); 859 } 860 } 861 862 public float getInertiaTime() { 863 return inertiaTime; 864 } 865 866 public void setInertiaTime(float inertiaTime) { 867 this.inertiaTime = inertiaTime; 868 } 869 870 871} 872 873 874 875 876 877 878