001package com.fs.starfarer.api.impl.campaign.intel; 002 003import java.util.Set; 004 005import java.awt.Color; 006 007import org.lwjgl.input.Keyboard; 008 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.campaign.FactionAPI; 011import com.fs.starfarer.api.campaign.InteractionDialogAPI; 012import com.fs.starfarer.api.campaign.RepLevel; 013import com.fs.starfarer.api.campaign.SectorEntityToken; 014import com.fs.starfarer.api.campaign.TextPanelAPI; 015import com.fs.starfarer.api.campaign.econ.MarketAPI; 016import com.fs.starfarer.api.campaign.econ.MonthlyReport; 017import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode; 018import com.fs.starfarer.api.campaign.listeners.CommissionEndedListener; 019import com.fs.starfarer.api.campaign.listeners.EconomyTickListener; 020import com.fs.starfarer.api.characters.PersonAPI; 021import com.fs.starfarer.api.impl.campaign.econ.EstablishedPolity; 022import com.fs.starfarer.api.impl.campaign.ids.Factions; 023import com.fs.starfarer.api.impl.campaign.ids.People; 024import com.fs.starfarer.api.impl.campaign.ids.Tags; 025import com.fs.starfarer.api.impl.campaign.intel.contacts.ContactIntel; 026import com.fs.starfarer.api.impl.campaign.intel.events.EstablishedPolityScript; 027import com.fs.starfarer.api.impl.campaign.rulecmd.HA_CMD; 028import com.fs.starfarer.api.impl.campaign.shared.SharedData; 029import com.fs.starfarer.api.ui.ButtonAPI; 030import com.fs.starfarer.api.ui.IntelUIAPI; 031import com.fs.starfarer.api.ui.LabelAPI; 032import com.fs.starfarer.api.ui.SectorMapAPI; 033import com.fs.starfarer.api.ui.TooltipMakerAPI; 034import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 035import com.fs.starfarer.api.util.Misc; 036 037public class PerseanLeagueMembership extends BaseIntelPlugin implements EconomyTickListener, CommissionEndedListener { 038 039 public static enum AgreementEndingType { 040 EXPIRED, 041 BROKEN, 042 ENDED, 043 } 044 045 public static int TIMES_LEFT_LEAGUE_FOR_NO_REJOIN = 2; 046 047 // in $player memory 048 public static final String PL_MEMBERSHIP_GOOD_DEAL = "$hasGoodPLMembershipDeal"; 049 public static final String PAYING_HOUSE_HANNAN = "$payingHouseHannan"; 050 public static final String LEFT_LEAGUE_WHEN_GOOD_DEAL = "$leftLeagueWhenGoodDeal"; 051 public static final String IS_LEAGUE_MEMBER = "$isLeagueMember"; 052 public static final String NUM_TIMES_LEFT_LEAGUE = "$numTimesLeftLeague"; 053 054 public static final String DEFEATED_BLOCKADE = "$defeatedLeagueBlockade"; 055 public static final String DEFEATED_PUN_EX = "$defeatedLeaguePunEx"; 056 057 public static int getNumTimesLeftLeague() { 058 return Global.getSector().getPlayerMemoryWithoutUpdate().getInt(NUM_TIMES_LEFT_LEAGUE); 059 } 060 public static void incrLeftLeagueCount() { 061 int count = getNumTimesLeftLeague(); 062 Global.getSector().getPlayerMemoryWithoutUpdate().set(NUM_TIMES_LEFT_LEAGUE, count + 1); 063 } 064 065 public static boolean isDefeatedBlockadeOrPunEx() { 066 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(DEFEATED_BLOCKADE) || 067 Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(DEFEATED_PUN_EX); 068 } 069 public static void setDefeatedBlockade(boolean value) { 070 Global.getSector().getPlayerMemoryWithoutUpdate().set(DEFEATED_BLOCKADE, value); 071 if (!value) { 072 Global.getSector().getPlayerMemoryWithoutUpdate().unset(DEFEATED_BLOCKADE); 073 } 074 } 075 public static void setDefeatedPunEx(boolean value) { 076 Global.getSector().getPlayerMemoryWithoutUpdate().set(DEFEATED_PUN_EX, value); 077 if (!value) { 078 Global.getSector().getPlayerMemoryWithoutUpdate().unset(DEFEATED_PUN_EX); 079 } 080 } 081 public static boolean isGoodDeal() { 082 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(PL_MEMBERSHIP_GOOD_DEAL); 083 } 084 085 public static boolean isPayingHouseHannan() { 086 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(PAYING_HOUSE_HANNAN); 087 } 088 public static void setPayingHouseHannan(boolean value) { 089 Global.getSector().getPlayerMemoryWithoutUpdate().set(PAYING_HOUSE_HANNAN, value); 090 if (!value) { 091 Global.getSector().getPlayerMemoryWithoutUpdate().unset(PAYING_HOUSE_HANNAN); 092 } 093 } 094 public static boolean isLeftLeagueWhenGoodDeal() { 095 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(LEFT_LEAGUE_WHEN_GOOD_DEAL); 096 } 097 public static void setLeftLeagueWhenGoodDeal(boolean value) { 098 Global.getSector().getPlayerMemoryWithoutUpdate().set(LEFT_LEAGUE_WHEN_GOOD_DEAL, value); 099 if (!value) { 100 Global.getSector().getPlayerMemoryWithoutUpdate().unset(LEFT_LEAGUE_WHEN_GOOD_DEAL); 101 } 102 } 103 public static boolean isLeagueMember() { 104 return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(IS_LEAGUE_MEMBER); 105 } 106 public static void setLeagueMember(boolean member) { 107 Global.getSector().getPlayerMemoryWithoutUpdate().set(IS_LEAGUE_MEMBER, member); 108 } 109 110 111 public static String KEY = "$plMembership_ref"; 112 public static PerseanLeagueMembership get() { 113 return (PerseanLeagueMembership) Global.getSector().getMemoryWithoutUpdate().get(KEY); 114 } 115 116 public static String BUTTON_END = "End"; 117 public static String BUTTON_RENEGE_HANNAN = "Renege Hannan"; 118 119 public static String UPDATE_PARAM_ACCEPTED = "update_param_accepted"; 120 121 122 protected FactionAPI faction = null; 123 protected AgreementEndingType endType = null; 124 125 public PerseanLeagueMembership(InteractionDialogAPI dialog) { 126 this.faction = Global.getSector().getFaction(Factions.PERSEAN); 127 128 setImportant(true); 129 setLeagueMember(true); 130 new EstablishedPolityScript(); 131 132 Global.getSector().getPlayerFaction().setSecondaryColorOverride(getFaction().getBaseUIColor()); 133 Global.getSector().getPlayerFaction().setSecondaryColorSegmentsOverride(8); 134 135 TextPanelAPI text = null; 136 if (dialog != null) text = dialog.getTextPanel(); 137 138 Global.getSector().getListenerManager().addListener(this); 139 Global.getSector().getMemoryWithoutUpdate().set(KEY, this); 140 141 Global.getSector().getIntelManager().addIntel(this, true); 142 143 RepLevel level = faction.getRelToPlayer().getLevel(); 144 if (!level.isAtWorst(RepLevel.NEUTRAL)) { 145 Misc.adjustRep(Factions.PERSEAN, 2f, RepLevel.NEUTRAL, text); 146 } 147 148 sendUpdate(UPDATE_PARAM_ACCEPTED, text); 149 } 150 151 @Override 152 protected void notifyEnding() { 153 super.notifyEnding(); 154 155 setLeagueMember(false); 156 setLeftLeagueWhenGoodDeal(isGoodDeal()); 157 //setPayingHouseHannan(false); 158 stopPayingHouseHannan(false, null); 159 160 Global.getSector().getMemoryWithoutUpdate().unset(KEY); 161 162 Global.getSector().getListenerManager().removeListener(this); 163 } 164 165 @Override 166 protected void notifyEnded() { 167 super.notifyEnded(); 168 } 169 170 protected Object readResolve() { 171 return this; 172 } 173 174 public String getBaseName() { 175 return "Persean League Membership"; 176 } 177 178 public String getAcceptedPostfix() { 179 return "Accepted"; 180 } 181 182 public String getBrokenPostfix() { 183 return "Annulled"; 184 185 } 186 public String getEndedPostfix() { 187 return "Ended"; 188 } 189 190 public String getExpiredPostfix() { 191 return "Expired"; 192 } 193 194 public String getName() { 195 String postfix = ""; 196 if (isEnding() && endType != null) { 197 switch (endType) { 198 case BROKEN: 199 postfix = " - " + getBrokenPostfix(); 200 break; 201 case ENDED: 202 postfix = " - " + getEndedPostfix(); 203 break; 204 case EXPIRED: 205 postfix = " - " + getExpiredPostfix(); 206 break; 207 } 208 } 209 if (isSendingUpdate() && getListInfoParam() == UPDATE_PARAM_ACCEPTED) { 210 postfix = " - " + getAcceptedPostfix(); 211 } 212 return getBaseName() + postfix; 213 } 214 215 @Override 216 public FactionAPI getFactionForUIColors() { 217 return faction; 218 } 219 220 public String getSmallDescriptionTitle() { 221 return getName(); 222 } 223 224 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 225 Color h = Misc.getHighlightColor(); 226 Color g = Misc.getGrayColor(); 227 float pad = 3f; 228 float opad = 10f; 229 230 float initPad = pad; 231 if (mode == ListInfoMode.IN_DESC) initPad = opad; 232 233 Color tc = getBulletColorForMode(mode); 234 235 bullet(info); 236 boolean isUpdate = getListInfoParam() != null; 237 238// if (getListInfoParam() == UPDATE_PARAM_ACCEPTED) { 239// return; 240// } 241 242 int perMonth = computeCreditsPerMonth(); 243 if (perMonth != 0 && !isEnded() && !isEnding()) { 244 Color c = perMonth > 0 ? h : Misc.getNegativeHighlightColor(); 245 info.addPara("%s per month", initPad, tc, c, Misc.getDGSCredits(perMonth)); 246 } 247 248 unindent(info); 249 } 250 251// @Override 252// public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 253// Color h = Misc.getHighlightColor(); 254// Color g = Misc.getGrayColor(); 255// Color c = getTitleColor(mode); 256// float pad = 3f; 257// float opad = 10f; 258// 259// info.addPara(getName(), c, 0f); 260// 261// addBulletPoints(info, mode); 262// } 263 264 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 265 Color h = Misc.getHighlightColor(); 266 Color g = Misc.getGrayColor(); 267 Color tc = Misc.getTextColor(); 268 float pad = 3f; 269 float opad = 10f; 270 271 Color c = faction.getBaseUIColor(); 272 273 info.addImage(getFaction().getLogo(), width, 128, opad); 274 275 if (isEnding() || isEnded()) { 276 info.addPara("Your agreement with the League is no longer in force.", opad, c, "League"); 277 return; 278 } 279 280 if (isGoodDeal()) { 281 info.addPara("You've negotiated a good membership deal from a position of strength. " 282 + "The League will support you politically, which " 283 + "for example makes it untenable for the Hegemony to insist on AI inspections " 284 + "in your space.", opad, c, "League"); 285 286 if (isPayingHouseHannan()) { 287 info.addPara("Part of your arrangement involves unofficial payments directly to the accounts " 288 + "of House Hannan, as payment for facilitating this agreement.", opad); 289 addBulletPoints(info, ListInfoMode.IN_DESC); 290 ButtonAPI button = info.addButton("Renege on the payments", BUTTON_RENEGE_HANNAN, 291 getFactionForUIColors().getBaseUIColor(), getFactionForUIColors().getDarkUIColor(), 292 (int)(width), 20f, opad * 1f); 293 button.setShortcut(Keyboard.KEY_G, true); 294 info.addSpacer(opad); 295 } 296 } else { 297 info.addPara("You've joined the Persean League, though it's whispered that you only did so to avoid " 298 + "harassment, and your membership dues are nothing more than protection payments. " 299 + "However, the League still supports you politically, which " 300 + "for example makes it untenable for the Hegemony to insist on AI inspections " 301 + "in your space.", opad, c, "Persean League"); 302 } 303 304 info.addPara("Due to being new members of the League, your colonies enjoy an " 305 + "increased flow of trade, resulting in a %s accessibility bonus.", 306 opad, h, 307 "+" + (int)Math.round(EstablishedPolity.ACCESSIBILITY_BONUS * 100f) + "%"); 308 309 info.addPara("Your League membership is contingent on maintaining an active Persean League commission. If " 310 + "you resign it, or if it is annulled for any reason, your membership will end as well.", opad); 311 312 if (computeCreditsPerMonth() != 0 && !isPayingHouseHannan()) { 313 info.addPara("You are paying membership dues to the League.", opad); 314 addBulletPoints(info, ListInfoMode.IN_DESC); 315 } 316 317 318 ButtonAPI button = info.addButton("End League membership", BUTTON_END, 319 getFactionForUIColors().getBaseUIColor(), getFactionForUIColors().getDarkUIColor(), 320 (int)(width), 20f, opad * 1f); 321 button.setShortcut(Keyboard.KEY_U, true); 322 323 } 324 325 326 public String getIcon() { 327 return faction.getCrest(); 328 } 329 330 public Set<String> getIntelTags(SectorMapAPI map) { 331 Set<String> tags = super.getIntelTags(map); 332 tags.add(Tags.INTEL_AGREEMENTS); 333 tags.add(faction.getId()); 334 return tags; 335 } 336 337 @Override 338 public String getImportantIcon() { 339 return Global.getSettings().getSpriteName("intel", "important_accepted_mission"); 340 } 341 342 @Override 343 public SectorEntityToken getMapLocation(SectorMapAPI map) { 344 return null; 345 } 346 347 348 public void reportEconomyMonthEnd() { 349 350 } 351 352 public void reportEconomyTick(int iterIndex) { 353 354 int credits = computeCreditsPerTick(); 355 356 if (credits != 0) { 357 FDNode node = getMonthlyReportNode(); 358 if (credits > 0) { 359 node.income += credits; 360 } else if (credits < 0) { 361 node.upkeep -= credits; 362 } 363 } 364 } 365 366 public FDNode getMonthlyReportNode() { 367 MonthlyReport report = SharedData.getData().getCurrentReport(); 368 FDNode marketsNode = report.getNode(MonthlyReport.OUTPOSTS); 369 if (marketsNode.name == null) { 370 marketsNode.name = "Colonies"; 371 marketsNode.custom = MonthlyReport.OUTPOSTS; 372 marketsNode.tooltipCreator = report.getMonthlyReportTooltip(); 373 } 374 375 FDNode paymentNode = report.getNode(marketsNode, "persean_league_membership"); 376 paymentNode.name = "Persean League membership dues"; 377 //paymentNode.upkeep += payment; 378 //paymentNode.icon = Global.getSettings().getSpriteName("income_report", "generic_expense"); 379 paymentNode.icon = faction.getCrest(); 380 381 if (paymentNode.tooltipCreator == null) { 382 paymentNode.tooltipCreator = new TooltipCreator() { 383 public boolean isTooltipExpandable(Object tooltipParam) { 384 return false; 385 } 386 public float getTooltipWidth(Object tooltipParam) { 387 return 450; 388 } 389 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 390 tooltip.addPara("Monthly dues for keeping your Persean League membership in good standing.", 0f); 391 } 392 }; 393 } 394 395 return paymentNode; 396 } 397 398 public int computeCreditsPerMonth() { 399 int perTick = computeCreditsPerTick(); 400 float numIter = Global.getSettings().getFloat("economyIterPerMonth"); 401 return (int) (perTick * numIter); 402 } 403 404 public static int computeCreditsPerTick() { 405 float numIter = Global.getSettings().getFloat("economyIterPerMonth"); 406 float f = 1f / numIter; 407 408 int payment = 0; 409 float feeFraction = Global.getSettings().getFloat("perseanLeagueFeeFraction"); 410 if (isGoodDeal()) { 411 feeFraction = 0f; 412 } 413 if (isPayingHouseHannan()) { 414 feeFraction += Global.getSettings().getFloat("houseHannanFeeFraction"); 415 } 416 417 for (MarketAPI market : Misc.getPlayerMarkets(false)) { 418 payment += (int) (market.getGrossIncome() * f) * feeFraction; 419 } 420 421 return -payment; 422 } 423 424 public FactionAPI getFaction() { 425 return faction; 426 } 427 428 public void endMembership(AgreementEndingType type, InteractionDialogAPI dialog) { 429 if (!isEnded() && !isEnding()) { 430 boolean baseMembership = !PerseanLeagueMembership.isGoodDeal(); 431 432 endType = type; 433 setImportant(false); 434 //endAfterDelay(); 435 endImmediately(); 436 437 incrLeftLeagueCount(); 438 439 Global.getSector().getPlayerFaction().setSecondaryColorOverride(null); 440 Global.getSector().getPlayerFaction().setSecondaryColorSegmentsOverride(0); 441 442 if (dialog != null) { 443 sendUpdate(new Object(), dialog.getTextPanel()); 444 } 445 446 if (baseMembership) { 447 HA_CMD.sendPerseanLeaguePunitiveExpedition(dialog); 448 } 449 450 } 451 } 452 453 454 public static void stopPayingHouseHannan(boolean trustBroken, InteractionDialogAPI dialog) { 455 if (isPayingHouseHannan()) { 456 TextPanelAPI text = null; 457 if (dialog != null) text = dialog.getTextPanel(); 458 PersonAPI reynard = People.getPerson(People.REYNARD_HANNAN); 459 if (reynard != null) { 460 Misc.adjustRep(reynard, -0.5f, text); 461 ContactIntel.removeContact(reynard, dialog); 462 } 463 setPayingHouseHannan(false); 464 465 new GensHannanMachinations(dialog); 466 if (trustBroken) { 467 Misc.incrUntrustwortyCount(); 468 } 469 } 470 } 471 472 @Override 473 public void buttonPressConfirmed(Object buttonId, IntelUIAPI ui) { 474 if (buttonId == BUTTON_END) { 475 endMembership(AgreementEndingType.ENDED, null); 476 } else if (buttonId == BUTTON_RENEGE_HANNAN) { 477 stopPayingHouseHannan(true, null); 478 } 479 super.buttonPressConfirmed(buttonId, ui); 480 } 481 482 483 @Override 484 public void createConfirmationPrompt(Object buttonId, TooltipMakerAPI prompt) { 485 FactionAPI faction = getFactionForUIColors(); 486 487 if (buttonId == BUTTON_END) { 488 if (isGoodDeal()) { 489 String extra = ""; 490 LabelAPI label = prompt.addPara("You've negotiated a good membership deal from a position of strength. If " 491 + "you leave, you will not be allowed to rejoin, but the " + 492 faction.getDisplayNameWithArticle() + " will likely refrain " 493 + "from harassing your interests." + extra, 0f, 494 Misc.getTextColor(), faction.getBaseUIColor(), 495 faction.getDisplayNameWithArticleWithoutArticle()); 496 label.setHighlightColors(Misc.getNegativeHighlightColor(), faction.getBaseUIColor()); 497 label.setHighlight("will not be allowed to rejoin", faction.getDisplayNameWithArticleWithoutArticle()); 498 if (isPayingHouseHannan()) { 499 prompt.addPara("However, Gens Hannan will not be pleased with their payments being stopped.", 500 10f, Misc.getNegativeHighlightColor(), "Gens Hannan will not be pleased"); 501 } 502 } else { 503 String extra = ""; 504 boolean canRejoin = getNumTimesLeftLeague() < TIMES_LEFT_LEAGUE_FOR_NO_REJOIN - 1; 505 float rejoinPad = 10f; 506 if (HA_CMD.canSendPerseanLeaguePunitiveExpedition()) { 507 LabelAPI label = prompt.addPara( 508 "If you leave, " + faction.getDisplayNameWithArticle() + " will likely take" 509 + " drastic action against your colonies." + extra, 0f); 510 label.setHighlightColors(faction.getBaseUIColor(), Misc.getNegativeHighlightColor()); 511 label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), "drastic action"); 512 } else { 513 if (canRejoin) { 514 prompt.addPara("If you leave, " + faction.getDisplayNameWithArticle() + " will likely start " 515 + " harassing your interests in the near future.", 0f, 516 Misc.getTextColor(), faction.getBaseUIColor(), 517 faction.getDisplayNameWithArticleWithoutArticle()); 518 } else { 519 rejoinPad = 0f; 520 } 521 } 522 if (!canRejoin) { 523 prompt.addPara("Given your history, it is likely that you would not be allowed " 524 + "to rejoin the League at any point in the future.", 525 rejoinPad, Misc.getNegativeHighlightColor(), 526 "would not be allowed to rejoin"); 527 } 528 } 529 } else if (buttonId == BUTTON_RENEGE_HANNAN) { 530 prompt.addPara("Gens Hannan will not be pleased. While they are unlikely to be unable to obtain your outright " 531 + "explulsion from the League, their influence is significant and will likely be used " 532 + "to continually undermine your standing. ", 0f, 533 Misc.getNegativeHighlightColor(), 534 "continually undermine your standing"); 535 536 prompt.addPara("You will also lose Reynard Hannan as a contact.", 10f, 537 Misc.getNegativeHighlightColor(), 538 "lose Reynard Hannan"); 539 } 540 } 541 542 @Override 543 public boolean doesButtonHaveConfirmDialog(Object buttonId) { 544 if (buttonId == BUTTON_END 545 || buttonId == BUTTON_RENEGE_HANNAN) { 546 return true; 547 } 548 return super.doesButtonHaveConfirmDialog(buttonId); 549 } 550 551 552 public void reportCommissionEnded(FactionCommissionIntel intel) { 553 if (intel.getFaction().getId().equals(Factions.PERSEAN)) { 554 endMembership(AgreementEndingType.BROKEN, null); 555 } 556 557 } 558} 559 560 561 562 563 564