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