001package com.fs.starfarer.api.impl.campaign.intel;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.LinkedHashMap;
006import java.util.LinkedHashSet;
007import java.util.List;
008import java.util.Set;
009
010import org.apache.log4j.Logger;
011
012import com.fs.starfarer.api.EveryFrameScript;
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.InteractionDialogAPI;
019import com.fs.starfarer.api.campaign.RepLevel;
020import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult;
021import com.fs.starfarer.api.campaign.SectorEntityToken;
022import com.fs.starfarer.api.campaign.econ.MarketAPI;
023import com.fs.starfarer.api.campaign.econ.MonthlyReport;
024import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode;
025import com.fs.starfarer.api.campaign.listeners.EconomyTickListener;
026import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
027import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
028import com.fs.starfarer.api.fleet.FleetMemberAPI;
029import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin;
030import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact;
031import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
032import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
033import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
034import com.fs.starfarer.api.impl.campaign.ids.Tags;
035import com.fs.starfarer.api.impl.campaign.rulecmd.missions.Commission;
036import com.fs.starfarer.api.impl.campaign.shared.SharedData;
037import com.fs.starfarer.api.ui.SectorMapAPI;
038import com.fs.starfarer.api.ui.TooltipMakerAPI;
039import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator;
040import com.fs.starfarer.api.util.Misc;
041
042public class FactionCommissionIntel extends BaseMissionIntel implements EveryFrameScript, 
043                                                                                        FleetEventListener, EconomyTickListener {
044        public static Logger log = Global.getLogger(FactionCommissionIntel.class);
045        
046        public static String UPDATE_PARAM_ACCEPTED = "update_param_accepted";
047        
048        public static class CommissionBountyResult extends MissionResult {
049                public float fraction;
050                public CommissionBountyResult(int payment, float fraction, ReputationAdjustmentResult rep) {
051                        super(payment, rep);
052                        this.fraction = fraction;
053                }
054        }
055        
056        public static class RepChangeData {
057                public FactionAPI faction;
058                public float delta;
059        }
060        
061        
062        protected float baseBounty = 0;
063        protected FactionAPI faction = null;
064        //protected FactionCommissionPlugin plugin = null;
065        
066        protected CommissionBountyResult latestResult;
067        
068        protected LinkedHashMap<String, RepChangeData> repChanges = new LinkedHashMap<String, RepChangeData>();
069        
070        public FactionCommissionIntel(FactionAPI faction) {
071                this.faction = faction;
072                baseBounty = Global.getSettings().getFloat("factionCommissionBounty");
073        }
074        
075        protected Object readResolve() {
076                baseBounty = Global.getSettings().getFloat("factionCommissionBounty");
077                return this;
078        }
079        
080        @Override
081        public void advanceMission(float amount) {
082                float days = Global.getSector().getClock().convertToDays(amount);
083
084                RepLevel level = faction.getRelToPlayer().getLevel();
085                if (!level.isAtWorst(RepLevel.NEUTRAL)) {
086                        setMissionResult(new MissionResult(-1, null));
087                        setMissionState(MissionState.COMPLETED);
088                        endMission();
089                        sendUpdateIfPlayerHasIntel(missionResult, false);
090                } else {
091                        makeRepChanges(null);
092                }
093        }
094        
095        
096        @Override
097        public void missionAccepted() {
098                log.info(String.format("Accepted commission with [%s]", faction.getDisplayName(), (int) baseBounty));
099                
100                setImportant(true);
101                setMissionState(MissionState.ACCEPTED);
102                
103                Global.getSector().getIntelManager().addIntel(this, true);
104                Global.getSector().getListenerManager().addListener(this);
105                Global.getSector().addScript(this);
106                
107                Global.getSector().getCharacterData().getMemoryWithoutUpdate().set(MemFlags.FCM_FACTION, faction.getId());
108                Global.getSector().getCharacterData().getMemoryWithoutUpdate().set(MemFlags.FCM_EVENT, this);
109        }
110        
111        @Override
112        public void endMission() {
113                endMission(null);
114        }
115        public void endMission(InteractionDialogAPI dialog) {
116                log.info(String.format("Ending commission with [%s]", faction.getDisplayName()));
117                Global.getSector().getListenerManager().removeListener(this);
118                Global.getSector().removeScript(this);
119                
120                Global.getSector().getCharacterData().getMemoryWithoutUpdate().unset(MemFlags.FCM_FACTION);
121                Global.getSector().getCharacterData().getMemoryWithoutUpdate().unset(MemFlags.FCM_EVENT);
122                
123                undoAllRepChanges(dialog);
124                
125                //endAfterDelay();
126                endImmediately();
127                
128                ListenerUtil.reportCommissionEnded(this);
129        }
130        
131        public void makeRepChanges(InteractionDialogAPI dialog) {       
132                FactionAPI player = Global.getSector().getPlayerFaction();
133                for (FactionAPI other : getRelevantFactions()) {
134                        RepChangeData change = repChanges.get(other.getId());
135
136                        boolean madeHostile = change != null;
137                        boolean factionHostile = faction.isHostileTo(other);
138                        boolean playerHostile = player.isHostileTo(other); 
139                        
140                        if (factionHostile && !playerHostile && !madeHostile) {
141                                makeHostile(other, dialog);
142                        }
143                        
144                        if (!factionHostile && madeHostile) {
145                                undoRepChange(other, dialog);
146                        }
147                }
148        }
149        
150        public void makeHostile(FactionAPI other, InteractionDialogAPI dialog) {
151                ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation(
152                                new RepActionEnvelope(RepActions.MAKE_HOSTILE_AT_BEST, 
153                                null, null, dialog != null ? dialog.getTextPanel() : null, false, true), 
154                                other.getId());
155                
156                RepChangeData data = new RepChangeData();
157                data.faction = other;
158                data.delta = rep.delta;
159                repChanges.put(other.getId(), data);
160        }
161        
162        public void undoRepChange(FactionAPI other, InteractionDialogAPI dialog) {
163                String id = other.getId();
164                RepChangeData change = repChanges.get(id);
165                
166                if (change == null) return;
167                
168                CustomRepImpact impact = new CustomRepImpact();
169                impact.delta = -change.delta;
170                impact.delta = Math.max(0f, impact.delta - Global.getSettings().getFloat("factionCommissionRestoredRelationshipPenalty"));
171                if (impact.delta > 0) {
172                        Global.getSector().adjustPlayerReputation(
173                                        new RepActionEnvelope(RepActions.CUSTOM, 
174                                                        impact, null, dialog != null ? dialog.getTextPanel() : null, false, true), 
175                                                        id);
176                }
177                repChanges.remove(id);
178        }
179        
180        public void undoAllRepChanges(InteractionDialogAPI dialog) {
181                for (RepChangeData data : new ArrayList<RepChangeData>(repChanges.values())) {
182                        undoRepChange(data.faction, dialog);
183                }
184        }
185        
186        
187        public List<FactionAPI> getRelevantFactions() {
188                Set<FactionAPI> factions = new LinkedHashSet<FactionAPI>();
189                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
190                        if (market.isHidden()) continue;
191                        FactionAPI curr = market.getFaction();
192                        if (factions.contains(curr)) continue;
193                        
194                        if (curr.isShowInIntelTab()) {
195                                factions.add(curr);
196                        }
197                }
198        
199                return new ArrayList<FactionAPI>(factions);
200        }
201        public List<FactionAPI> getHostileFactions() {
202                FactionAPI player = Global.getSector().getPlayerFaction();
203                List<FactionAPI> hostile = new ArrayList<FactionAPI>();
204                for (FactionAPI other : getRelevantFactions()) {
205                        if (this.faction.isHostileTo(other)) {
206                                hostile.add(other);
207                        }
208                }
209                return hostile;
210        }
211        
212
213        public float computeStipend() {
214                float level = Global.getSector().getPlayerPerson().getStats().getLevel();
215                
216                return Global.getSettings().getFloat("factionCommissionStipendBase") +
217                           Global.getSettings().getFloat("factionCommissionStipendPerLevel") * level;
218        }
219        
220        public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
221        }
222
223        public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) {
224                if (isEnded() || isEnding()) return;
225                
226                if (!battle.isPlayerInvolved()) return;
227                
228                int payment = 0;
229                float fpDestroyed = 0;
230                for (CampaignFleetAPI otherFleet : battle.getNonPlayerSideSnapshot()) {
231                        if (!faction.isHostileTo(otherFleet.getFaction())) continue;
232                        
233                        float bounty = 0;
234                        for (FleetMemberAPI loss : Misc.getSnapshotMembersLost(otherFleet)) {
235                                float mult = Misc.getSizeNum(loss.getHullSpec().getHullSize());
236                                bounty += mult * baseBounty;
237                                fpDestroyed += loss.getFleetPointCost();
238                        }
239                        
240                        payment += (int) (bounty * battle.getPlayerInvolvementFraction());
241                }
242        
243                if (payment > 0) {
244                        Global.getSector().getPlayerFleet().getCargo().getCredits().add(payment);
245                        
246                        float repFP = (int)(fpDestroyed * battle.getPlayerInvolvementFraction());
247                        ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation(
248                                                        new RepActionEnvelope(RepActions.COMMISSION_BOUNTY_REWARD, new Float(repFP), null, null, true, false), 
249                                                        faction.getId());
250                        latestResult = new CommissionBountyResult(payment, battle.getPlayerInvolvementFraction(), rep);
251                        sendUpdateIfPlayerHasIntel(latestResult, false);
252                }
253        }
254        
255        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
256                
257                Color h = Misc.getHighlightColor();
258                Color g = Misc.getGrayColor();
259                float pad = 3f;
260                float opad = 10f;
261                
262                float initPad = pad;
263                if (mode == ListInfoMode.IN_DESC) initPad = opad;
264                
265                Color tc = getBulletColorForMode(mode);
266                
267                bullet(info);
268                boolean isUpdate = getListInfoParam() != null;
269                
270                if (getListInfoParam() == UPDATE_PARAM_ACCEPTED) {
271                        return;
272                }
273                
274                if (missionResult != null && missionResult.payment < 0) {
275//                      info.addPara("Annulled by " + faction.getDisplayNameWithArticle(), initPad, tc,
276//                                      faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle());
277                } else if (isUpdate && latestResult != null) {
278                        info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(latestResult.payment));
279                        if (Math.round(latestResult.fraction * 100f) < 100f) {
280                                info.addPara("%s share based on damage dealt", 0f, tc, h, 
281                                                "" + (int) Math.round(latestResult.fraction * 100f) + "%");
282                        }
283                        CoreReputationPlugin.addAdjustmentMessage(latestResult.rep1.delta, faction, null, 
284                                                                                                          null, null, info, tc, isUpdate, 0f);
285                } else if (mode == ListInfoMode.IN_DESC) {
286                        info.addPara("%s base bounty per hostile frigate", initPad, tc, h, Misc.getDGSCredits(baseBounty));
287                        info.addPara("%s monthly stipend", 0f, tc, h, Misc.getDGSCredits(computeStipend()));
288                } else {
289//                      info.addPara("Faction: " + faction.getDisplayName(), initPad, tc,
290//                                      faction.getBaseUIColor(), faction.getDisplayName());
291//                      initPad = 0f;
292                        info.addPara("%s base reward per frigate", initPad, tc, h, Misc.getDGSCredits(baseBounty));
293                        info.addPara("%s monthly stipend", 0f, tc, h, Misc.getDGSCredits(computeStipend()));
294                }
295                unindent(info);
296        }
297        
298        @Override
299        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
300                Color h = Misc.getHighlightColor();
301                Color g = Misc.getGrayColor();
302                Color c = getTitleColor(mode);
303                float pad = 3f;
304                float opad = 10f;
305                
306                info.addPara(getName(), c, 0f);
307                
308                addBulletPoints(info, mode);
309        }
310        
311        public String getSortString() {
312                return "Commission";
313        }
314        
315        public String getName() {
316                String prefix = Misc.ucFirst(faction.getPersonNamePrefix()) + " Commission";
317//              if (plugin != null) {
318//                      String override = plugin.getNameOverride();
319//                      if (override != null) {
320//                              prefix = override;
321//                      }
322//              }
323                if (isEnding()) {
324                        if (missionResult != null && missionResult.payment < 0) {
325                                if (isSendingUpdate()) {
326                                        return prefix + " - Annulled";
327                                }
328                                return prefix + " - Annulled";
329                                //return prefix + " (Annulled)";
330                        }
331                        //return prefix + " (Resigned)";
332                        return prefix + " - Resigned";
333                }
334                if (isSendingUpdate() && getListInfoParam() == UPDATE_PARAM_ACCEPTED) {
335                        //return prefix + " Accepted";
336                        return prefix + " - Accepted";
337                }
338                return prefix;
339        }
340        
341        @Override
342        public FactionAPI getFactionForUIColors() {
343                return faction;
344        }
345        
346        public FactionAPI getFaction() {
347                return faction;
348        }
349
350        public String getSmallDescriptionTitle() {
351                return getName();
352        }
353        
354        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
355                createSmallDescription(info, width, height, false);
356        }
357        public void createSmallDescription(TooltipMakerAPI info, float width, float height, 
358                                                                           boolean forMarketConditionTooltip) {
359                Color h = Misc.getHighlightColor();
360                Color g = Misc.getGrayColor();
361                Color tc = Misc.getTextColor();
362                float pad = 3f;
363                float opad = 10f;
364
365                info.addImage(faction.getLogo(), width, 128, opad);
366                
367                
368                if (isEnding()) {
369                        if (missionResult != null && missionResult.payment < 0) {
370                                info.addPara("Your commission was annulled by " + faction.getDisplayNameWithArticle() + 
371                                                " due to your standing falling too low.",
372                                                 opad, faction.getBaseUIColor(),
373                                                 faction.getDisplayNameWithArticleWithoutArticle());
374                                
375                                CoreReputationPlugin.addRequiredStanding(faction, Commission.COMMISSION_REQ, null, null, info, tc, opad, true);
376                                CoreReputationPlugin.addCurrentStanding(faction, null, null, info, tc, opad);
377                        } else {
378                                info.addPara("You've resigned your commission with " + faction.getDisplayNameWithArticle() + 
379                                                ".",
380                                                 opad, faction.getBaseUIColor(),
381                                                 faction.getDisplayNameWithArticleWithoutArticle());
382                        }
383                } else {
384                        info.addPara("You've accepted a %s commission.",
385                                        opad, faction.getBaseUIColor(), Misc.ucFirst(faction.getPersonNamePrefix()));
386                        
387                        addBulletPoints(info, ListInfoMode.IN_DESC);
388                        
389                        info.addPara("The combat bounty payment depends on the number and size of ships destroyed.", opad);
390                }
391                
392                if (latestResult != null) {
393                        //Color color = faction.getBaseUIColor();
394                        //Color dark = faction.getDarkUIColor();
395                        //info.addSectionHeading("Most Recent Reward", color, dark, Alignment.MID, opad);
396                        info.addPara("Most recent bounty:", opad);
397                        bullet(info);
398                        info.addPara("%s received", opad, tc, h, Misc.getDGSCredits(latestResult.payment));
399                        if (Math.round(latestResult.fraction * 100f) < 100f) {
400                                info.addPara("%s share based on damage dealt", 0f, tc, h, 
401                                                "" + (int) Math.round(latestResult.fraction * 100f) + "%");
402                        }
403                        CoreReputationPlugin.addAdjustmentMessage(latestResult.rep1.delta, faction, null, 
404                                                                                                          null, null, info, tc, false, 0f);
405                        unindent(info);
406                }
407
408                if (!isEnding() && !isEnded()) {
409                        boolean plMember = PerseanLeagueMembership.isLeagueMember();
410                        if (!plMember) {
411                                addAbandonButton(info, width, "Resign commission");
412                        } else {
413                                info.addPara("You can not resign your commission while polities under your "
414                                                + "control are members of the League.", opad);
415                        }
416                }
417        }
418        
419        public String getIcon() {
420                return faction.getCrest();
421        }
422        
423        public Set<String> getIntelTags(SectorMapAPI map) {
424                Set<String> tags = super.getIntelTags(map);
425                tags.remove(Tags.INTEL_ACCEPTED);
426                tags.remove(Tags.INTEL_MISSIONS);
427                //tags.add(Tags.INTEL_COMMISSION);
428                tags.add(Tags.INTEL_AGREEMENTS);
429                tags.add(faction.getId());
430                return tags;
431        }
432
433        @Override
434        public SectorEntityToken getMapLocation(SectorMapAPI map) {
435                return null;
436        }
437
438        @Override
439        protected MissionResult createAbandonedResult(boolean withPenalty) {
440                return createResignedCommissionResult(true, false, null);
441        }
442        
443        public MissionResult createResignedCommissionResult(boolean withPenalty, boolean inPerson, InteractionDialogAPI dialog) {
444                if (withPenalty) {
445                        CustomRepImpact impact = new CustomRepImpact();
446                        impact.delta = -1f * Global.getSettings().getFloat("factionCommissionResignPenalty");
447                        if (inPerson) {
448                                impact.delta = -1f * Global.getSettings().getFloat("factionCommissionResignPenaltyInPerson");
449                        }
450                        ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation(
451                                        new RepActionEnvelope(RepActions.CUSTOM, 
452                                                        impact, null, dialog != null ? dialog.getTextPanel() : null, false, true), 
453                                                        faction.getId());
454                        return new MissionResult();
455                }
456                return new MissionResult();
457        }
458
459        @Override
460        protected MissionResult createTimeRanOutFailedResult() {
461                return new MissionResult();
462        }
463
464        @Override
465        protected String getMissionTypeNoun() {
466                return "commission";
467        }
468
469        @Override
470        protected float getNoPenaltyAbandonDays() {
471                return 0f;
472        }
473
474
475
476        public void reportEconomyMonthEnd() {
477//              if (plugin != null) {
478//                      plugin.reportEconomyMonthEnd();
479//              }
480        }
481
482        public void reportEconomyTick(int iterIndex) {
483                
484                float numIter = Global.getSettings().getFloat("economyIterPerMonth");
485                float f = 1f / numIter;
486                
487                //CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
488                MonthlyReport report = SharedData.getData().getCurrentReport();
489                
490                FDNode fleetNode = report.getNode(MonthlyReport.FLEET);
491                fleetNode.name = "Fleet";
492                fleetNode.custom = MonthlyReport.FLEET;
493                fleetNode.tooltipCreator = report.getMonthlyReportTooltip();
494                
495                float stipend = computeStipend();
496                FDNode stipendNode = report.getNode(fleetNode, "node_id_stipend_" + faction.getId());
497                stipendNode.income += stipend * f;
498                
499                if (stipendNode.name == null) {
500                        stipendNode.name = faction.getDisplayName() + " Commission";
501                        stipendNode.icon = faction.getCrest();
502                        stipendNode.tooltipCreator = new TooltipCreator() {
503                                public boolean isTooltipExpandable(Object tooltipParam) {
504                                        return false;
505                                }
506                                public float getTooltipWidth(Object tooltipParam) {
507                                        return 450;
508                                }
509                                public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
510                                        tooltip.addPara("Your monthly stipend for holding a " + faction.getDisplayName() + " commission", 0f);
511                                }
512                        };
513                }
514                
515//              if (plugin != null) {
516//                      plugin.reportEconomyTick(iterIndex);
517//              }
518        }
519
520//      public FactionCommissionPlugin getPlugin() {
521//              return plugin;
522//      }
523//
524//      public void setPlugin(FactionCommissionPlugin plugin) {
525//              this.plugin = plugin;
526//      }
527        
528        
529}
530
531
532
533
534
535