001package com.fs.starfarer.api.impl.campaign.intel.inspection; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Random; 007import java.util.Set; 008 009import org.lwjgl.input.Keyboard; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.CampaignFleetAPI; 013import com.fs.starfarer.api.campaign.FactionAPI; 014import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult; 015import com.fs.starfarer.api.campaign.SectorEntityToken; 016import com.fs.starfarer.api.campaign.econ.Industry; 017import com.fs.starfarer.api.campaign.econ.MarketAPI; 018import com.fs.starfarer.api.characters.PersonAPI; 019import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin; 020import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact; 021import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 022import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 023import com.fs.starfarer.api.impl.campaign.DebugFlags; 024import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator; 025import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 026import com.fs.starfarer.api.impl.campaign.ids.Factions; 027import com.fs.starfarer.api.impl.campaign.ids.Tags; 028import com.fs.starfarer.api.impl.campaign.intel.raid.RaidAssignmentAI; 029import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel; 030import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidDelegate; 031import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI; 032import com.fs.starfarer.api.ui.Alignment; 033import com.fs.starfarer.api.ui.ButtonAPI; 034import com.fs.starfarer.api.ui.IntelUIAPI; 035import com.fs.starfarer.api.ui.LabelAPI; 036import com.fs.starfarer.api.ui.SectorMapAPI; 037import com.fs.starfarer.api.ui.TooltipMakerAPI; 038import com.fs.starfarer.api.util.Misc; 039 040public class HegemonyInspectionIntel extends RaidIntel implements RaidDelegate { 041 042 //public static float DEFAULT_INSPECTION_GROUND_STRENGTH = 1000; 043 044 public static interface InspectionEndedListener { 045 public void notifyInspectionEnded(HegemonyInspectionOutcome outcome); 046 } 047 048 public static enum HegemonyInspectionOutcome { 049 COLONY_NO_LONGER_EXISTS, 050 TASK_FORCE_DESTROYED, // rep hit due to destruction, depends on transponder status and other factors 051 CONFISCATE_CORES, // cores taken, minor rep hit 052 FOUND_EVIDENCE_NO_CORES, // player removed cores - lots of disruption 053 //CORES_HIDDEN, // player invested into hiding cores - costs credits, no rep hit 054 BRIBED, // player paid a lot of bribe money, no rep hit 055 } 056 057 public static enum AntiInspectionOrders { 058 COMPLY, 059 HIDE, 060 BRIBE, 061 RESIST, 062 } 063 064 public static final String BUTTON_CHANGE_ORDERS = "BUTTON_CHANGE_ORDERS"; 065 066 public static final Object MADE_HOSTILE_UPDATE = new Object(); 067 public static final Object ENTERED_SYSTEM_UPDATE = new Object(); 068 public static final Object OUTCOME_UPDATE = new Object(); 069 070 071 protected HIActionStage action; 072 073 protected AntiInspectionOrders orders = AntiInspectionOrders.COMPLY; 074 protected int investedCredits = 0; 075 protected MarketAPI target; 076 protected FactionAPI targetFaction; 077 protected MarketAPI from; 078 079 protected List<String> expectedCores = new ArrayList<String>(); 080 protected boolean enteredSystem = false; 081 protected HegemonyInspectionOutcome outcome; 082 protected Random random = new Random(); 083 084 protected InspectionEndedListener listener; 085 086 public HegemonyInspectionIntel(MarketAPI from, MarketAPI target, float inspectionFP) { 087 super(target.getStarSystem(), from.getFaction(), null); 088 this.delegate = this; 089 this.from = from; 090 this.target = target; 091 targetFaction = target.getFaction(); 092 093 for (Industry curr : target.getIndustries()) { 094 String id = curr.getAICoreId(); 095 if (id != null) { 096 expectedCores.add(id); 097 } 098 } 099 PersonAPI admin = target.getAdmin(); 100 if (admin.isAICore()) { 101 expectedCores.add(admin.getAICoreId()); 102 } 103 104 float orgDur = 20f + 10f * (float) Math.random(); 105 if (Global.getSettings().isDevMode()) { 106 orgDur = 1f; 107 } 108 109 if (DebugFlags.HEGEMONY_INSPECTION_DEBUG || DebugFlags.FAST_RAIDS) orgDur = 0.5f; 110 addStage(new HIOrganizeStage(this, from, orgDur)); 111 112 SectorEntityToken gather = from.getPrimaryEntity(); 113 SectorEntityToken raidJump = RouteLocationCalculator.findJumpPointToUse(getFactionForUIColors(), target.getPrimaryEntity()); 114 115 if (gather == null || raidJump == null) { 116 endImmediately(); 117 return; 118 } 119 120 float successMult = 0.5f; 121 122 HIAssembleStage assemble = new HIAssembleStage(this, gather); 123 assemble.addSource(from); 124 assemble.setSpawnFP(inspectionFP); 125 assemble.setAbortFP(inspectionFP * successMult); 126 addStage(assemble); 127 128 129 130 HITravelStage travel = new HITravelStage(this, gather, raidJump, false); 131 travel.setAbortFP(inspectionFP * successMult); 132 addStage(travel); 133 134 action = new HIActionStage(this, target); 135 action.setAbortFP(inspectionFP * successMult); 136 addStage(action); 137 138 addStage(new HIReturnStage(this)); 139 140 setImportant(true); 141 142 Global.getSector().getIntelManager().addIntel(this); 143 } 144 145 public InspectionEndedListener getListener() { 146 return listener; 147 } 148 149 150 public void setListener(InspectionEndedListener listener) { 151 this.listener = listener; 152 } 153 154 155 156 public Random getRandom() { 157 return random; 158 } 159 160 public MarketAPI getTarget() { 161 return target; 162 } 163 164 public MarketAPI getFrom() { 165 return from; 166 } 167 168 public RouteFleetAssignmentAI createAssignmentAI(CampaignFleetAPI fleet, RouteData route) { 169 RaidAssignmentAI raidAI = new RaidAssignmentAI(fleet, route, action); 170 //raidAI.setDelegate(action); 171 return raidAI; 172 } 173 174 public AntiInspectionOrders getOrders() { 175 return orders; 176 } 177 178 public void setOrders(AntiInspectionOrders orders) { 179 this.orders = orders; 180 } 181 182 public List<String> getExpectedCores() { 183 return expectedCores; 184 } 185 186 public int getInvestedCredits() { 187 return investedCredits; 188 } 189 190 public void setInvestedCredits(int investedCredits) { 191 this.investedCredits = investedCredits; 192 } 193 194 public boolean isEnteredSystem() { 195 return enteredSystem; 196 } 197 198 public void setEnteredSystem(boolean enteredSystem) { 199 this.enteredSystem = enteredSystem; 200 } 201 202 public HegemonyInspectionOutcome getOutcome() { 203 return outcome; 204 } 205 206 public void setOutcome(HegemonyInspectionOutcome outcome) { 207 this.outcome = outcome; 208 } 209 210 protected transient String targetOwner = null; 211 @Override 212 protected void advanceImpl(float amount) { 213 super.advanceImpl(amount); 214 if (target != null && targetOwner == null) targetOwner = target.getFactionId(); 215 if (failStage < 0 && targetOwner != null && target != null && !targetOwner.equals(target.getFactionId())) { 216 forceFail(false); 217 } 218 } 219 220 221 222 223 protected transient ReputationAdjustmentResult repResult = null; 224 public void makeHostileAndSendUpdate() { 225 boolean hostile = getFaction().isHostileTo(Factions.PLAYER); 226 if (!hostile) { 227 repResult = Global.getSector().adjustPlayerReputation( 228 new RepActionEnvelope(RepActions.MAKE_HOSTILE_AT_BEST, 229 null, null, null, false, false), 230 Factions.HEGEMONY); 231 sendUpdateIfPlayerHasIntel(MADE_HOSTILE_UPDATE, false); 232 } 233 } 234 235 public void sendInSystemUpdate() { 236 sendUpdateIfPlayerHasIntel(ENTERED_SYSTEM_UPDATE, false); 237 } 238 239 public void applyRepPenalty(float delta) { 240 CustomRepImpact impact = new CustomRepImpact(); 241 impact.delta = delta; 242 repResult = Global.getSector().adjustPlayerReputation( 243 new RepActionEnvelope(RepActions.CUSTOM, 244 impact, null, null, false, false), 245 getFaction().getId()); 246 } 247 248 public void sendOutcomeUpdate() { 249 sendUpdateIfPlayerHasIntel(OUTCOME_UPDATE, false); 250 } 251 252 @Override 253 public String getName() { 254 String base = "Hegemony AI Inspection"; 255 if (outcome == HegemonyInspectionOutcome.TASK_FORCE_DESTROYED || 256 outcome == HegemonyInspectionOutcome.COLONY_NO_LONGER_EXISTS) return base + " - Failed"; 257 if (outcome != null) return base + " - Completed"; 258 return base; 259 } 260 261 262 @Override 263 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 264 //super.addBulletPoints(info, mode); 265 266 Color h = Misc.getHighlightColor(); 267 Color g = Misc.getGrayColor(); 268 float pad = 3f; 269 float opad = 10f; 270 271 float initPad = pad; 272 if (mode == ListInfoMode.IN_DESC) initPad = opad; 273 274 Color tc = getBulletColorForMode(mode); 275 276 bullet(info); 277 boolean isUpdate = getListInfoParam() != null; 278 279 boolean hostile = getFaction().isHostileTo(Factions.PLAYER); 280 if (hostile) { 281 orders = AntiInspectionOrders.RESIST; 282 } 283 284 if (getListInfoParam() == MADE_HOSTILE_UPDATE) { 285 FactionAPI other = target.getFaction(); 286 info.addPara("Target: %s", initPad, tc, 287 other.getBaseUIColor(), target.getName()); 288 initPad = 0f; 289 info.addPara("" + faction.getDisplayName() + " forces arrive in-system and encounter resistance", initPad, tc, 290 faction.getBaseUIColor(), faction.getDisplayName()); 291 initPad = 0f; 292 CoreReputationPlugin.addAdjustmentMessage(repResult.delta, faction, null, 293 null, null, info, tc, isUpdate, initPad); 294 return; 295 } 296 297 if (getListInfoParam() == ENTERED_SYSTEM_UPDATE) { 298 FactionAPI other = target.getFaction(); 299 info.addPara("Target: %s", initPad, tc, 300 other.getBaseUIColor(), target.getName()); 301 initPad = 0f; 302 info.addPara("Arrived in-system", tc, initPad); 303// info.addPara("" + faction.getDisplayName() + " inspection arrives in-system", initPad, tc, 304// faction.getBaseUIColor(), faction.getDisplayName()); 305 return; 306 } 307 308 if (getListInfoParam() == OUTCOME_UPDATE) { 309 int num = getActionStage().getCoresRemoved().size(); 310 if (num > 0) { 311 String cores = "cores"; 312 if (num == 1) cores = "core"; 313 info.addPara("%s AI " + cores + " confiscated", initPad, tc, h, "" + num); 314 initPad = 0f; 315 } 316 if (outcome == HegemonyInspectionOutcome.BRIBED) { 317 info.addPara("No AI cores found", initPad, tc, h, "" + num); 318 } else if (outcome == HegemonyInspectionOutcome.FOUND_EVIDENCE_NO_CORES) { 319 FactionAPI other = target.getFaction(); 320 info.addPara("Operations at %s disrupted", initPad, tc, 321 other.getBaseUIColor(), target.getName()); 322 //info.addPara("Operations disrupted by inspection", initPad, h, "" + num); 323 } else if (outcome == HegemonyInspectionOutcome.CONFISCATE_CORES) { 324 } 325 initPad = 0f; 326 if (repResult != null) { 327 CoreReputationPlugin.addAdjustmentMessage(repResult.delta, faction, null, 328 null, null, info, tc, isUpdate, initPad); 329 } 330 return; 331 } 332 333// if (getListInfoParam() == UPDATE_FAILED) { 334// FactionAPI other = target.getFaction(); 335// info.addPara("Target: %s", initPad, tc, 336// other.getBaseUIColor(), target.getName()); 337// initPad = 0f; 338// info.addPara("Inspection failed", tc, initPad); 339// return; 340// } 341 342 float eta = getETA(); 343 344 FactionAPI other = target.getFaction(); 345 info.addPara("Target: %s", initPad, tc, 346 other.getBaseUIColor(), target.getName()); 347 initPad = 0f; 348 349 if (eta > 1 && outcome == null) { 350 String days = getDaysString(eta); 351 info.addPara("Estimated %s " + days + " until arrival", 352 initPad, tc, h, "" + (int)Math.round(eta)); 353 initPad = 0f; 354 355 if (hostile || orders == AntiInspectionOrders.RESIST) { 356 info.addPara("Defenders will resist", tc, initPad); 357 } else if (orders == AntiInspectionOrders.COMPLY) { 358 info.addPara("Defenders will comply", tc, initPad); 359 } else if (orders == AntiInspectionOrders.BRIBE) { 360 info.addPara("Funds allocated for bribe", tc, initPad); 361 } 362 } else if (outcome == null && action.getElapsed() > 0) { 363 info.addPara("Inspection under way", tc, initPad); 364 initPad = 0f; 365 } else if (outcome != null) { 366 int num = getActionStage().getCoresRemoved().size(); 367 if (num > 0) { 368 String cores = "cores"; 369 if (num == 1) cores = "core"; 370 info.addPara("%s AI " + cores + " confiscated", initPad, tc, h, "" + num); 371 initPad = 0f; 372 } else if (outcome == HegemonyInspectionOutcome.TASK_FORCE_DESTROYED) { 373 //info.addPara("Inspection failed", tc, initPad); 374 } 375// info.addPara("Inspection under way", tc, initPad); 376// initPad = 0f; 377 } 378 379 unindent(info); 380 } 381 382 public HIActionStage getActionStage() { 383 for (RaidStage stage : stages) { 384 if (stage instanceof HIActionStage) { 385 return (HIActionStage) stage; 386 } 387 } 388 return null; 389// return (HIActionStage) stages.get(2); 390 } 391 392 @Override 393 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 394 super.createIntelInfo(info, mode); 395 } 396 397 @Override 398 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 399 //super.createSmallDescription(info, width, height); 400 401 Color h = Misc.getHighlightColor(); 402 Color g = Misc.getGrayColor(); 403 Color tc = Misc.getTextColor(); 404 float pad = 3f; 405 float opad = 10f; 406 407 info.addImage(getFactionForUIColors().getLogo(), width, 128, opad); 408 409 FactionAPI faction = getFaction(); 410 String has = faction.getDisplayNameHasOrHave(); 411 String is = faction.getDisplayNameIsOrAre(); 412 413 //AssembleStage as = getAssembleStage(); 414 //MarketAPI source = as.getSources().get(0); 415 416 String strDesc = getRaidStrDesc(); 417 int numFleets = (int) getOrigNumFleets(); 418 String fleets = "fleets"; 419 if (numFleets == 1) fleets = "fleet"; 420 421 LabelAPI label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + is + 422 " targeting %s for an inspection due to the suspected use of AI cores there." + 423 " The task force is projected to be " + strDesc + " and is likely comprised of " + 424 "" + numFleets + " " + fleets + ".", 425 opad, faction.getBaseUIColor(), target.getName()); 426 label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), target.getName(), strDesc, "" + numFleets); 427 label.setHighlightColors(faction.getBaseUIColor(), target.getFaction().getBaseUIColor(), h, h); 428 429 if (outcome == null) { 430 addStandardStrengthComparisons(info, target, targetFaction, true, false, "inspection", "inspection's"); 431 } 432 433 info.addSectionHeading("Status", 434 faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad); 435 436 for (RaidStage stage : stages) { 437 stage.showStageInfo(info); 438 if (getStageIndex(stage) == failStage) break; 439 } 440 441 442 if (outcome == null) { 443 FactionAPI pf = Global.getSector().getPlayerFaction(); 444 info.addSectionHeading("Your orders", 445 pf.getBaseUIColor(), pf.getDarkUIColor(), Alignment.MID, opad); 446 447 boolean hostile = getFaction().isHostileTo(Factions.PLAYER); 448 if (hostile) { 449 label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + is + 450 " hostile towards " + pf.getDisplayNameWithArticle() + ". Your forces will attempt to resist the inspection.", 451 opad); 452 label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), 453 pf.getDisplayNameWithArticleWithoutArticle()); 454 label.setHighlightColors(faction.getBaseUIColor(), pf.getBaseUIColor()); 455 } else { 456 switch (orders) { 457 case COMPLY: 458 info.addPara("The authorities at " + target.getName() + " will comply with the inspection. " + 459 "It is certain to find any AI cores currently in use.", opad); 460 break; 461 case BRIBE: 462 info.addPara("You've allocated enough funds to ensure the inspection " + 463 "will produce a satisfactory outcome all around.", opad); 464 break; 465 // case HIDE: 466 // info.addPara("You've allocated funds to improve AI core concealment measures. It is certain that the inspection " + 467 // "can be defeated, but some amount of suspicion will remain.", opad); 468 // break; 469 case RESIST: 470 info.addPara("Your space and ground forces will attempt to resist the inspection.", opad); 471 break; 472 } 473 474 if (!enteredSystem) { 475 ButtonAPI button = info.addButton("Change orders", BUTTON_CHANGE_ORDERS, 476 pf.getBaseUIColor(), pf.getDarkUIColor(), 477 (int)(width), 20f, opad * 2f); 478 button.setShortcut(Keyboard.KEY_T, true); 479 } else { 480 info.addPara("The inspection task force is in-system and there's no time to implement new orders.", opad); 481 } 482 } 483 } else { 484 //addBulletPoints(info, ListInfoMode.IN_DESC); 485 bullet(info); 486 if (repResult != null) { 487 CoreReputationPlugin.addAdjustmentMessage(repResult.delta, faction, null, 488 null, null, info, tc, false, opad); 489 } 490 unindent(info); 491 } 492 } 493 494 495 496 @Override 497 public void sendUpdateIfPlayerHasIntel(Object listInfoParam, boolean onlyIfImportant, boolean sendIfHidden) { 498 499 if (listInfoParam == UPDATE_RETURNING) { 500 // we're using sendOutcomeUpdate() to send an end-of-event update instead 501 return; 502 } 503 504 super.sendUpdateIfPlayerHasIntel(listInfoParam, onlyIfImportant, sendIfHidden); 505 } 506 507 @Override 508 public Set<String> getIntelTags(SectorMapAPI map) { 509 //return super.getIntelTags(map); 510 511 Set<String> tags = super.getIntelTags(map); 512 tags.add(Tags.INTEL_MILITARY); 513 tags.add(Tags.INTEL_COLONIES); 514 tags.add(getFaction().getId()); 515 return tags; 516 } 517 518 519 public void notifyRaidEnded(RaidIntel raid, RaidStageStatus status) { 520 if (outcome == null && failStage >= 0) { 521 if (!target.isInEconomy() || !target.isPlayerOwned()) { 522 outcome = HegemonyInspectionOutcome.COLONY_NO_LONGER_EXISTS; 523 } else { 524 outcome = HegemonyInspectionOutcome.TASK_FORCE_DESTROYED; 525 } 526 //sendOutcomeUpdate(); // don't do this - base raid sends an UPDATE_FAILED so we're good already 527 } 528 if (listener != null && outcome != null) { 529 listener.notifyInspectionEnded(outcome); 530 } 531 } 532 533 534 public void buttonPressConfirmed(Object buttonId, IntelUIAPI ui) { 535 if (buttonId == BUTTON_CHANGE_ORDERS) { 536 ui.showDialog(null, new HIOrdersInteractionDialogPluginImpl(this, ui)); 537 } 538 } 539 540 541 @Override 542 public String getIcon() { 543 return Global.getSettings().getSpriteName("intel", "hegemony_inspection"); 544 } 545 546 @Override 547 public SectorEntityToken getMapLocation(SectorMapAPI map) { 548 if (target != null && target.isInEconomy() && target.getPrimaryEntity() != null) { 549 return target.getPrimaryEntity(); 550 } 551 return super.getMapLocation(map); 552 } 553} 554 555 556 557 558 559