001package com.fs.starfarer.api.impl.campaign.intel.events;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Random;
007import java.util.Set;
008
009import java.awt.Color;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.campaign.InteractionDialogAPI;
013import com.fs.starfarer.api.campaign.TextPanelAPI;
014import com.fs.starfarer.api.campaign.listeners.EconomyTickListener;
015import com.fs.starfarer.api.impl.campaign.ids.Tags;
016import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
017import com.fs.starfarer.api.ui.Alignment;
018import com.fs.starfarer.api.ui.CustomPanelAPI;
019import com.fs.starfarer.api.ui.EventProgressBarAPI;
020import com.fs.starfarer.api.ui.SectorMapAPI;
021import com.fs.starfarer.api.ui.TooltipMakerAPI;
022import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator;
023import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipLocation;
024import com.fs.starfarer.api.ui.UIComponentAPI;
025import com.fs.starfarer.api.util.Misc;
026
027/**
028 * For capital-E "Events" with a progress bar, Outcomes, contributing factors, and so on.
029 * 
030 * Large UI; takes up the map area of the intel screen.
031 * 
032 * @author Alex
033 *
034 * Copyright 2022 Fractal Softworks, LLC
035 */
036public class BaseEventIntel extends BaseIntelPlugin implements EconomyTickListener {
037
038        /**
039         * Assigned to EventStageData.rollData when no random event was rolled.
040         */
041        public static final String RANDOM_EVENT_NONE = "random_event_none";
042        
043        public static enum StageIconSize {
044                SMALL,
045                MEDIUM,
046                LARGE,
047        }
048        public static enum RandomizedStageType {
049                GOOD,
050                BAD,
051                NEUTRAL,
052        }
053        
054        /**
055         * Just a data holder for display purposes in the BaseEventIntel UI.
056         */
057        public static class EventStageDisplayData {
058                public float size;
059                public float downLineLength;
060                public String icon;
061                public Color color;
062                public Color iconColor = Color.white;
063                public String label;
064                public Color labelColor;
065                public int importance = 0;
066        }
067
068        public static class EventStageData {
069                public Object id;
070                public int progress;
071                public boolean isOneOffEvent;
072                public boolean wasEverReached = false;
073                public boolean isRepeatable = true;
074                public boolean sendIntelUpdateOnReaching = true;
075                public boolean hideIconWhenPastStageUnlessLastActive = false;
076                public boolean keepIconBrightWhenLaterStageReached = false;
077                public StageIconSize iconSize = StageIconSize.MEDIUM;
078                
079                public boolean randomized = false;
080                public RandomizedStageType randomType = RandomizedStageType.NEUTRAL;
081                public int progressToResetAt;
082                public int progressToRollAt;
083                public Object rollData;
084                
085                public EventStageData(Object id, int progress, boolean isOneOffEvent) {
086                        this(id, progress, isOneOffEvent, StageIconSize.MEDIUM);
087                }
088                public EventStageData(Object id, int progress, boolean isOneOffEvent, StageIconSize iconSize) {
089                        this.id = id;
090                        this.progress = progress;
091                        this.isOneOffEvent = isOneOffEvent;
092                        this.iconSize = iconSize;
093                }
094                
095                public void addProgressReq(TooltipMakerAPI tooltip, float pad) {
096                        tooltip.addPara("Requires %s points of event progress.", 
097                                        pad, Misc.getHighlightColor(), "" + progress);
098                }
099                public void addResetReq(TooltipMakerAPI tooltip, float pad) {
100                        addResetReq(tooltip, false, pad);
101                }
102                public void beginResetReqList(TooltipMakerAPI tooltip, boolean withAvertInfo, float initPad) {
103                        beginResetReqList(tooltip, withAvertInfo, "outcome", initPad);
104                }
105                public void beginResetReqList(TooltipMakerAPI tooltip, boolean withAvertInfo, String outcome, float initPad) {
106                        float opad = 10f;
107                        float pad = 3f;
108                        tooltip.addPara("This " + outcome + " will be averted if:", initPad);
109                        tooltip.setBulletedListMode(BaseIntelPlugin.BULLET);
110                        if (withAvertInfo) {
111                                tooltip.addPara("Event progress is reduced to %s points or below",
112                                                pad, Misc.getHighlightColor(), "" + progressToResetAt);
113                        }
114                        
115                }
116                public void endResetReqList(TooltipMakerAPI tooltip, boolean withTriggeredResetInfo) {
117                        endResetReqList(tooltip, withTriggeredResetInfo, "outcome", -1, -1);
118                }
119                public void endResetReqList(TooltipMakerAPI tooltip, boolean withTriggeredResetInfo, String outcome, int min, int max) {
120                        tooltip.setBulletedListMode(null);
121                        if (withTriggeredResetInfo) {
122                                float opad = 10f;
123                                if (min < 0 || max < 0) {
124                                        tooltip.addPara("If this " + outcome + " is triggered, event progress will be reset to a much lower value afterwards.",
125                                                        opad);
126                                } else {
127                                        tooltip.addPara("If this " + outcome + " is triggered, event progress will be reset to between %s and %s points.",
128                                                        opad, Misc.getHighlightColor(), "" + min, "" + max);
129                                }
130                        }
131                }
132                
133                public void addResetReq(TooltipMakerAPI tooltip, boolean withResetIfTriggered, float pad) {
134                        addResetReq(tooltip, withResetIfTriggered, "outcome", -1, -1, pad);
135                }
136                public void addResetReq(TooltipMakerAPI tooltip, boolean withResetIfTriggered, String outcome, int min, int max, float pad) {
137                        if (withResetIfTriggered) {
138                                if (min < 0 || max < 0) {
139                                        tooltip.addPara("This " + outcome + " will be averted if event progress is reduced to %s points or below. "
140                                                        + "If this " + outcome + " is triggered, event progress will be reset to a lower value afterwards.",
141                                                        pad, Misc.getHighlightColor(), "" + progressToResetAt);
142                                } else {
143                                        tooltip.addPara("This " + outcome + " will be averted if event progress is reduced to %s points or below. "
144                                                        + "If this " + outcome + " is triggered, event progress will be reset to between %s and %s points.",
145                                                        pad, Misc.getHighlightColor(), "" + progressToResetAt, "" + min, "" + max);
146                                }
147                        } else {
148                                tooltip.addPara("This " + outcome + " will be averted if event progress is reduced to %s points or below.", 
149                                                pad, Misc.getHighlightColor(), "" + progressToResetAt);
150                        }
151                }
152        }
153
154        protected int progress = 0;
155        protected int maxProgress = 1000;
156        
157        //protected Object startingStage;
158        protected List<EventStageData> stages = new ArrayList<EventStageData>();
159        protected IntelSortTier sortTier;
160        
161        protected List<EventFactor> factors = new ArrayList<EventFactor>();
162        protected Random random = new Random();
163        
164        protected float progressDeltaRemainder = 0f;
165        protected transient float uiWidth;
166        
167        public BaseEventIntel() {
168                setSortTier(IntelSortTier.TIER_2);
169                
170                Global.getSector().addScript(this);
171                // this needs to be done in sub-classes since it sends out an intel update
172                // and that won't have the right data because the event isn't finished
173                // being constructed here - it needs stages etc added to it
174                //Global.getSector().getIntelManager().addIntel(this);
175                Global.getSector().getListenerManager().addListener(this);
176        }
177
178        @Override
179        protected void advanceImpl(float amount) {
180                super.advanceImpl(amount);
181                
182                List<EventFactor> remove = new ArrayList<EventFactor>();
183                for (EventFactor curr : factors) {
184                        if (curr.isExpired()) {
185                                remove.add(curr);
186                                continue;
187                        }
188                        
189                        curr.advance(amount);
190                        
191                        if (curr.isExpired()) {
192                                remove.add(curr);
193                        }
194                }
195                factors.removeAll(remove);
196                for (EventFactor factor : remove) {
197                        factor.notifyFactorRemoved();
198                }
199        }
200
201
202
203        @Override
204        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
205                Color c = getTitleColor(mode);
206                boolean large = true;
207                if (large) info.setParaSmallInsignia();
208                info.addPara(getName(), c, 0f);
209                //info.addPara("Hostile Activity", c, 0f);
210                if (large) info.setParaFontDefault();
211                addBulletPoints(info, mode);
212        }
213        
214        protected boolean addEventFactorBulletPoints(TooltipMakerAPI info, ListInfoMode mode, boolean isUpdate, 
215                           Color tc, float initPad) {
216                if (isUpdate && getListInfoParam() instanceof EventFactor) {
217                        EventFactor factor = (EventFactor) getListInfoParam();
218                        if (factor.isOneTime()) {
219                                factor.addBulletPointForOneTimeFactor(this, info, tc, initPad);
220                        }
221                        return true;
222                }
223                return false;
224        }
225
226
227        @Override
228        public void createLargeDescription(CustomPanelAPI panel, float width, float height) {
229                
230                float opad = 10f;
231                uiWidth = width;
232
233                // TODO DEBUG
234                //setProgress(900);
235                //setProgress((int) (0 + (float) Math.random() * 400));
236                //setProgress(900);
237                //setProgress(499);
238                //setProgress(200);
239                //setProgress(0);
240                
241                TooltipMakerAPI main = panel.createUIElement(width, height, true);
242
243                main.setTitleOrbitronVeryLarge();
244                main.addTitle(getName(), Misc.getBasePlayerColor());
245                
246                EventProgressBarAPI bar = main.addEventProgressBar(this, 100f);
247                TooltipCreator barTC = getBarTooltip();
248                if (barTC != null) {
249                        main.addTooltipToPrevious(barTC, TooltipLocation.BELOW, false);
250                }
251                
252                for (EventStageData curr : stages) {
253                        if (curr.progress <= 0) continue; // no icon for "starting" stage
254                        //if (curr.rollData == null || curr.rollData.equals(RANDOM_EVENT_NONE)) continue;
255                        if (RANDOM_EVENT_NONE.equals(curr.rollData)) continue;
256                        if (curr.wasEverReached && curr.isOneOffEvent && !curr.isRepeatable) continue;
257                        
258                        if (curr.hideIconWhenPastStageUnlessLastActive && 
259                                        curr.progress <= progress &&
260                                        getLastActiveStage(true) != curr) {
261                                continue;
262                        }
263                        
264                        EventStageDisplayData data = createDisplayData(curr.id);
265                        UIComponentAPI marker = main.addEventStageMarker(data);
266                        float xOff = bar.getXCoordinateForProgress(curr.progress) - bar.getPosition().getX();
267                        marker.getPosition().aboveLeft(bar, data.downLineLength).setXAlignOffset(xOff - data.size / 2f - 1);
268                        
269                        TooltipCreator tc = getStageTooltip(curr.id);
270                        if (tc != null) {
271                                main.addTooltipTo(tc, marker, TooltipLocation.LEFT, false); 
272                        }
273                }
274                
275                // progress indicator
276                {
277                        UIComponentAPI marker = main.addEventProgressMarker(this);
278                        float xOff = bar.getXCoordinateForProgress(progress) - bar.getPosition().getX();
279                        marker.getPosition().belowLeft(bar, -getBarProgressIndicatorHeight() * 0.5f - 2)
280                                                .setXAlignOffset(xOff - getBarProgressIndicatorWidth() / 2 - 1);
281                }
282
283                main.addSpacer(opad);
284                main.addSpacer(opad);
285                for (EventStageData curr : stages) {
286                        if (curr.wasEverReached && curr.isOneOffEvent && !curr.isRepeatable) continue;
287                        addStageDescriptionWithImage(main, curr.id);
288                }
289                
290
291                afterStageDescriptions(main);
292                
293                float barW = getBarWidth();
294                float factorWidth = (barW - opad) / 2f;
295                
296                if (withMonthlyFactors() != withOneTimeFactors()) {
297                        //factorWidth = barW;
298                        factorWidth = (int) (barW * 0.6f);
299                }
300                
301                TooltipMakerAPI mFac = main.beginSubTooltip(factorWidth);
302                
303                Color c = getFactionForUIColors().getBaseUIColor();
304                Color bg = getFactionForUIColors().getDarkUIColor();
305                mFac.addSectionHeading("Monthly factors", c, bg, Alignment.MID, opad).getPosition().setXAlignOffset(0);
306                
307                float strW = 40f;
308                float rh = 20f;
309                //rh = 15f;
310                mFac.beginTable2(getFactionForUIColors(), rh, false, false, 
311                                "Monthly factors", factorWidth - strW - 3,
312                                "Progress", strW
313                                );
314                
315                for (EventFactor factor : factors) {
316                        if (factor.isOneTime()) continue;
317                        if (!factor.shouldShow(this)) continue;
318                        
319                        String desc = factor.getDesc(this);
320                        if (desc != null) {
321                                mFac.addRowWithGlow(Alignment.LMID, factor.getDescColor(this), desc,
322                                                                    Alignment.RMID, factor.getProgressColor(this), factor.getProgressStr(this));
323                                TooltipCreator t = factor.getMainRowTooltip(this);
324                                if (t != null) {
325                                        mFac.addTooltipToAddedRow(t, TooltipLocation.RIGHT, false);
326                                }
327                        }
328                        factor.addExtraRows(mFac, this);
329                }
330                
331                //mFac.addButton("TEST", new String(), factorWidth, 20f, opad);
332                mFac.addTable("None", -1, opad);
333                mFac.getPrev().getPosition().setXAlignOffset(-5);
334                
335                main.endSubTooltip();
336                
337                TooltipMakerAPI oFac = main.beginSubTooltip(factorWidth);
338                
339                oFac.addSectionHeading("Recent one-time factors", c, bg, Alignment.MID, opad).getPosition().setXAlignOffset(0);
340                
341                oFac.beginTable2(getFactionForUIColors(), 20f, false, false,
342                                "One-time factors", factorWidth - strW - 3,
343                                "Progress", strW
344                                );
345                
346                List<EventFactor> reversed = new ArrayList<EventFactor>(factors);
347                Collections.reverse(reversed);
348                for (EventFactor factor : reversed) {
349                        if (!factor.isOneTime()) continue;
350                        if (!factor.shouldShow(this)) continue;
351                        
352                        String desc = factor.getDesc(this);
353                        if (desc != null) {
354                                oFac.addRowWithGlow(Alignment.LMID, factor.getDescColor(this), desc,
355                                                                    Alignment.RMID, factor.getProgressColor(this), factor.getProgressStr(this));
356                                TooltipCreator t = factor.getMainRowTooltip(this);
357                                if (t != null) {
358                                        oFac.addTooltipToAddedRow(t, TooltipLocation.LEFT);
359                                }
360                        }
361                        factor.addExtraRows(oFac, this);
362                }
363                
364                oFac.addTable("None", -1, opad);
365                oFac.getPrev().getPosition().setXAlignOffset(-5);
366                main.endSubTooltip();
367                
368                
369                float factorHeight = Math.max(mFac.getHeightSoFar(), oFac.getHeightSoFar());
370                mFac.setHeightSoFar(factorHeight);
371                oFac.setHeightSoFar(factorHeight);
372                
373                
374                if (withMonthlyFactors() && withOneTimeFactors()) {
375                        main.addCustom(mFac, opad * 2f);
376                        main.addCustomDoNotSetPosition(oFac).getPosition().rightOfTop(mFac, opad);
377                } else if (withMonthlyFactors()) {
378                        main.addCustom(mFac, opad * 2f);
379                } else if (withOneTimeFactors()) {
380                        main.addCustom(oFac, opad * 2f);
381                }
382                
383                //main.addButton("TEST", new String(), factorWidth, 20f, opad);
384                
385                panel.addUIElement(main).inTL(0, 0);
386        }
387        
388        public TooltipCreator getBarTooltip() {
389                return new TooltipCreator() {
390                        public boolean isTooltipExpandable(Object tooltipParam) {
391                                return false;
392                        }
393                        public float getTooltipWidth(Object tooltipParam) {
394                                return 450;
395                        }
396                        
397                        public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
398                                float opad = 10f;
399                                Color h = Misc.getHighlightColor();
400                                
401                                tooltip.addPara("Event progress: %s out of %s points.", 0f, h, "" + progress, "" + maxProgress);
402                                int p = getMonthlyProgress();
403                                String pStr = "" + p;
404                                if (p > 0) pStr = "+" + p;
405                                tooltip.addPara("Projected monthly progress: %s points.", opad, getProgressColor(p), pStr);
406                                
407                                tooltip.addPara("Event progress is influenced by various factors. Some of these apply over time, "
408                                                + "and some only apply once. As the event progresses, "
409                                                + "different stages and outcomes may unfold.", opad);
410                        }
411                };
412        }
413        public TooltipCreator getStageTooltip(Object stageId) {
414                final EventStageData esd = getDataFor(stageId);
415                if (esd == null || (esd.randomized && (esd.rollData == null || RANDOM_EVENT_NONE.equals(esd.rollData)))) {
416                        return new TooltipCreator() {
417                                public boolean isTooltipExpandable(Object tooltipParam) {
418                                        return false;
419                                }
420                                public float getTooltipWidth(Object tooltipParam) {
421                                        return BaseEventFactor.TOOLTIP_WIDTH;
422                                }
423                                
424                                public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
425                                        float opad = 10f;
426                                        Color h = Misc.getHighlightColor();
427                                        
428                                        tooltip.addPara("Something might occur when event progress reaches %s points. "
429                                                        + "What that is, if anything, will be determined when event progress reaches "
430                                                        + "%s points.", 0f, 
431                                                        h, "" + esd.progress, "" + esd.progressToRollAt);
432                                        
433                                        if (esd.isRepeatable) {
434                                                tooltip.addPara("This event is repeatable.", opad);
435                                        } else {
436                                                tooltip.addPara("This event is not repeatable.", opad);
437                                        }
438                                }
439                        };
440                }
441                return getStageTooltipImpl(stageId);
442        }
443        public TooltipCreator getStageTooltipImpl(Object stageId) {
444                return null;
445        }
446        
447        public float getImageSizeForStageDesc(Object stageId) {
448                return 64f;
449        }
450        public float getImageIndentForStageDesc(Object stageId) {
451                return 0f;
452        }
453        
454        public void afterStageDescriptions(TooltipMakerAPI main) {
455                
456        }
457        public void addStageDescriptionWithImage(TooltipMakerAPI main, Object stageId) {
458                EventStageDisplayData data = createDisplayData(stageId);
459                String icon;
460                if (data != null) {
461                        icon = data.icon;
462                } else {
463                        icon = getIcon();
464                }
465                float imageSize = getImageSizeForStageDesc(stageId);
466                float opad = 10f;
467                float indent = 0;
468                indent = 10f;
469                indent += getImageIndentForStageDesc(stageId);
470                float width = getBarWidth() - indent * 2f;
471                
472                
473                TooltipMakerAPI info = main.beginImageWithText(icon, imageSize, width, true);
474                //TooltipMakerAPI info = main.beginImageWithText("graphics/icons/missions/ga_intro.png", 64);
475                addStageDescriptionText(info, width - imageSize - opad, stageId);
476                if (info.getHeightSoFar() > 0) {
477                        main.addImageWithText(opad).getPosition().setXAlignOffset(indent);
478                        main.addSpacer(0).getPosition().setXAlignOffset(-indent);
479                }
480        }
481        
482        public void addStageDescriptionText(TooltipMakerAPI info, float width, Object stageId) {
483                
484        }
485        
486        
487        public EventStageDisplayData createDisplayData(Object stageId) {
488                EventStageDisplayData data = new EventStageDisplayData();
489                data.size = getStageIconSize(stageId);
490                data.downLineLength = getStageDownLineLength(stageId);
491                data.color = getStageColor(stageId);
492                data.icon = getStageIcon(stageId);
493                data.iconColor = getStageIconColor(stageId);
494                data.importance = getStageImportance(stageId);
495                data.label = getStageLabel(stageId);
496                data.labelColor = getStageLabelColor(stageId);
497                return data;
498        }
499        
500        protected String getStageIcon(Object stageId) {
501                EventStageData esd = getDataFor(stageId);
502                if (esd == null || (esd.randomized && (esd.rollData == null || RANDOM_EVENT_NONE.equals(esd.rollData)))) {
503                        if (esd.randomType == RandomizedStageType.GOOD) {
504                                return Global.getSettings().getSpriteName("events", "stage_unknown_good");
505                        } else if (esd.randomType == RandomizedStageType.BAD) {
506                                return Global.getSettings().getSpriteName("events", "stage_unknown_bad");
507                        }
508                        return Global.getSettings().getSpriteName("events", "stage_unknown_neutral");
509                }
510                return getStageIconImpl(stageId);
511                
512        }
513        protected String getStageIconImpl(Object stageId) {
514                return Global.getSettings().getSpriteName("events", "stage_unknown");
515        }
516        
517        protected float getStageIconSize(Object stageId) {
518                EventStageData esd = getDataFor(stageId);
519                if (esd != null && esd.iconSize == StageIconSize.SMALL) return 32;
520                if (esd != null && esd.iconSize == StageIconSize.LARGE) return 48;
521                return 40;
522                //return 32;
523        }
524        
525        protected float getStageDownLineLength(Object stageId) {
526                EventStageData esd = getDataFor(stageId);
527                //if (esd != null && esd.iconSize == StageIconSize.SMALL) return 24;
528                //if (esd != null && esd.iconSize == StageIconSize.SMALL) return 56; // level with top of LARGE
529                if (esd != null && esd.iconSize == StageIconSize.SMALL) return 48; // level with middle of LARGE
530                if (esd != null && esd.iconSize == StageIconSize.LARGE) return 40;
531                return 32;
532        }
533        
534
535        public float getBarWidth() {
536                //return uiWidth - 200f;
537                return 750f;
538        }
539        public float getBarHeight() {
540                return 20f;
541        }
542        
543        public boolean putBarProgressIndicatorLabelOnRight() {
544                float test = (float)progress / (float)maxProgress * getBarWidth();
545                return test < 50;
546        }
547        
548        public float getBarProgressIndicatorHeight() {
549                return 20f;
550        }
551        public float getBarProgressIndicatorWidth() {
552                return 20f;
553        }
554        public Color getBarProgressIndicatorLabelColor() {
555                return Misc.getHighlightColor();
556        }
557        public Color getBarProgressIndicatorColor() {
558                return getBarColor();
559        }
560        public Color getBarBracketColor() {
561                if (true) return Misc.getBasePlayerColor();
562                return getBarColor();
563        }
564        
565        public Color getBarColor() {
566                Color color = Misc.getBasePlayerColor();
567                color = Misc.interpolateColor(color, Color.black, 0.25f);
568                return color;
569        }
570        
571        protected Color getBaseStageColor(Object stageId) {
572                return getBarColor();
573        }
574        protected Color getDarkStageColor(Object stageId) {
575                Color color = getBarColor();
576                color = Misc.interpolateColor(color, Color.black, 0.5f);
577                return color;
578        }
579        protected Color getStageColor(Object stageId) {
580                int req = getRequiredProgress(stageId);
581                
582                EventStageData last = getLastActiveStage(false);
583                EventStageData esd = getDataFor(stageId);
584                boolean grayItOut = false;
585                if (last != null && esd != null && last != esd && !esd.isOneOffEvent &&
586                                !esd.keepIconBrightWhenLaterStageReached &&
587                                esd.progress < last.progress) {
588                        grayItOut = true;
589                }
590                
591                if (esd != null && esd.randomized && esd.rollData != null) {
592                        return getBaseStageColor(stageId);
593                }
594                
595                if (req > progress || grayItOut) {
596                        return getDarkStageColor(stageId);
597                }
598                return getBaseStageColor(stageId);
599        }
600        protected Color getStageIconColor(Object stageId) {
601                int req = getRequiredProgress(stageId);
602                
603                EventStageData last = getLastActiveStage(false);
604                EventStageData esd = getDataFor(stageId);
605                boolean grayItOut = false;
606                if (last != null && esd != null && last != esd && !esd.isOneOffEvent &&
607                                !esd.keepIconBrightWhenLaterStageReached && 
608                                esd.progress < last.progress) {
609                        grayItOut = true;
610                }
611                
612                if (esd != null && esd.randomized && esd.rollData != null) {
613                        return Color.white;
614                }
615                
616                if (req > progress || grayItOut) {
617                        return new Color(255,255,255,155);
618                }
619                return Color.white;
620        }
621        protected int getStageImportance(Object stageId) {
622                return 0;
623        }
624        protected String getStageLabel(Object stageId) {
625                Object least = null;
626                int min = Integer.MAX_VALUE;
627                for (EventStageData curr : stages) {
628                        int req = curr.progress;
629                        if (req > progress && req < min) {
630                                min = req;
631                                least = curr.id;
632                        }
633                }
634                if (stageId.equals(least)) {
635                        return "" + getRequiredProgress(least);
636                }
637                return null;
638        }
639        protected Color getStageLabelColor(Object stageId) {
640                return Misc.getHighlightColor();
641        }
642
643        @Override
644        public Set<String> getIntelTags(SectorMapAPI map) {
645                Set<String> tags = super.getIntelTags(map);
646                tags.add(Tags.INTEL_MAJOR_EVENT);
647                return tags;
648        }
649        
650        @Override
651        public boolean hasSmallDescription() {
652                return false;
653        }
654
655        @Override
656        public boolean hasLargeDescription() {
657                return true;
658        }
659
660        public int getMaxProgress() {
661                return maxProgress;
662        }
663
664        public void setMaxProgress(int maxProgress) {
665                this.maxProgress = maxProgress;
666        }
667
668        public List<EventStageData> getStages() {
669                return stages;
670        }
671        
672        
673        public boolean isStageOrOneOffEventReached(Object stageId) {
674                return progress >= getRequiredProgress(stageId);
675        }
676        
677        public boolean isStageActiveAndLast(Object stageId) {
678                return isStageActiveAndLast(stageId, false);
679        }
680        public boolean isStageActiveAndLast(Object stageId, boolean includeOneOffEvents) {
681                EventStageData data = getLastActiveStage(includeOneOffEvents);
682                //if (data == null) return startingStage == stageId;
683                if (data == null) return false;
684                return data.id == stageId; // assuming stageId will be enums, so == check is ok
685        }
686        
687//      public boolean isStageActive(Object stageId) {
688//              EventStageData data = getDataFor(stageId);
689//              if (data == null) return false;
690//              return data.progress <= getProgress();
691//      }
692        
693        
694//      public Object getStartingStage() {
695//              return startingStage;
696//      }
697//      public void setStartingStage(Object startingStage) {
698//              this.startingStage = startingStage;
699//      }
700        
701        public void addStage(Object id, int progress) {
702                addStage(id, progress, StageIconSize.MEDIUM);
703        }
704        public void addStage(Object id, int progress, StageIconSize iconSize) {
705                addStage(id, progress, false, iconSize);
706        }
707        public void addStage(Object id, int progress, boolean isOneOffEvent) {
708                addStage(id, progress, isOneOffEvent, StageIconSize.MEDIUM);
709        }
710        public void addStage(Object id, int progress, boolean isOneOffEvent, StageIconSize iconSize) {
711                stages.add(new EventStageData(id, progress, isOneOffEvent, iconSize));
712        }
713        
714        public boolean isStageActive(Object stageId) {
715                EventStageData data = getDataFor(stageId);
716                if (data == null) return false;
717                return data.progress <= getProgress();
718                
719        }
720        public EventStageData getLastActiveStage(boolean includeOneOffEvents) {
721                EventStageData last = null;
722                int max = Integer.MIN_VALUE;
723                for (EventStageData curr : stages) {
724                        if (!includeOneOffEvents && curr.isOneOffEvent) continue;
725                        
726                        int req = curr.progress;
727                        if (progress >= req && req > max) {
728                                max = req;
729                                last = curr;
730                        }
731                }
732                return last;
733        }
734        
735        public EventStageData getDataFor(Object stageId) {
736                for (EventStageData curr : stages) {
737                        if (stageId.equals(curr.id)) return curr;
738                }
739                return null;
740        }
741        
742        public int getRequiredProgress(Object stageId) {
743                //if (stageId == startingStage) return 0;
744                EventStageData data = getDataFor(stageId);
745                return data == null ? 0 : data.progress;
746        }
747        
748        public void setSortTier(IntelSortTier sortTier) {
749                this.sortTier = sortTier;
750        }
751
752        @Override
753        public IntelSortTier getSortTier() {
754                if (sortTier == null || isEnding() || isEnded()) return super.getSortTier();
755                return sortTier;
756        }
757        
758        
759        @Override
760        protected void notifyEnded() {
761                super.notifyEnded();
762                Global.getSector().removeScript(this);
763                for (EventFactor factor : factors) {
764                        factor.notifyEventEnded();
765                }
766        }
767
768
769        @Override
770        protected void notifyEnding() {
771                super.notifyEnding();
772//              for (MarketAPI curr : getAffectedMarkets()) {
773//                      if (curr.hasCondition(Conditions.HOSTILE_ACTIVITY)) {
774//                              curr.removeCondition(Conditions.HOSTILE_ACTIVITY);
775//                      }
776//              }
777                Global.getSector().getListenerManager().removeListener(this);
778                for (EventFactor factor : factors) {
779                        factor.notifyEventEnding();
780                }
781        }
782
783        public void addFactor(EventFactor factor) {
784                addFactor(factor, null);
785        }
786        /**
787         * Adds factor's progress to event progress if it's a one-time factor.
788         * If dialog is passed in, it'll be visible to notifyStageReached() via addingFactorDialog, 
789         * in case that needs to print an update there.
790         * @param factor
791         */
792        protected transient InteractionDialogAPI addingFactorDialog = null;
793        public void addFactor(EventFactor factor, InteractionDialogAPI dialog) {
794                addingFactorDialog = dialog;
795                factors.add(factor);
796                if (factor.isOneTime()) {
797                        if (factor.getProgress(this) != 0) {
798                                TextPanelAPI textPanel = dialog == null ? null : dialog.getTextPanel();
799                                sendUpdateIfPlayerHasIntel(factor, textPanel);
800                        }
801                        setProgress(getProgress() + factor.getProgress(this));
802                }
803                addingFactorDialog = null;
804        }
805        public TextPanelAPI getTextPanelForStageChange() {
806                if (addingFactorDialog == null) return null;
807                return addingFactorDialog.getTextPanel();
808        }
809        
810        public List<EventFactor> getFactors() {
811                return factors;
812        }
813        
814        public EventFactor getFactorOfClass(Class c) {
815                for (EventFactor f : getFactors()) {
816                        if (f.getClass() == c) {
817                                return f;
818                        }
819                }
820                return null;
821        }
822        
823        public void removeFactor(EventFactor factor) {
824                factors.remove(factor);
825                factor.notifyFactorRemoved();
826        }
827        
828        public void removeFactorOfClass(Class<EventFactor> c) {
829                List<EventFactor> remove = new ArrayList<EventFactor>();
830                for (EventFactor curr : factors) {
831                        if (c.isInstance(curr)) {
832                                remove.add(curr);
833                        }
834                }
835                factors.removeAll(remove);
836                for (EventFactor factor : remove) {
837                        factor.notifyFactorRemoved();
838                }
839        }
840        
841        public boolean isEventProgressANegativeThingForThePlayer() {
842                return false;
843        }
844
845        
846        public int getMaxMonthlyProgress() {
847                return 1000000;
848        }
849
850        public int getMonthlyProgress() {
851                int total = 0;
852                float mult = 1f;
853                for (EventFactor factor : factors) {
854                        if (factor.isOneTime()) continue;
855                        total += factor.getProgress(this);
856                        mult *= factor.getAllProgressMult(this);
857                }
858                
859                if (total != 0) {
860                        float sign = Math.signum(total);
861                        total = Math.round(sign * Math.abs(total) * mult);
862                        if (total == 0) total = (int) Math.round(1f * sign);
863                }
864                
865                total = Math.min(total, getMaxMonthlyProgress());
866                
867                return total;
868        }
869        
870        
871        public void reportEconomyTick(int iterIndex) {
872                float delta = getMonthlyProgress();
873                
874                if (delta <= 0 && this instanceof HostileActivityEventIntel) {
875                        setProgress(0);
876                        return;
877                }
878                
879                float numIter = Global.getSettings().getFloat("economyIterPerMonth");
880                float f = 1f / numIter;
881                
882                delta *= f;
883                delta += progressDeltaRemainder;
884                
885                int apply = (int) delta;
886
887                progressDeltaRemainder = delta - (float) apply;
888                
889                setProgress(progress + apply);
890                
891        }
892        
893        public int getProgress() {
894                return progress;
895        }
896
897        protected transient boolean prevProgressDeltaWasPositive = false;
898        public void setProgress(int progress) {
899                if (this.progress == progress) return;
900                
901                if (progress < 0) progress = 0;
902                if (progress > maxProgress) progress = maxProgress;
903                
904                EventStageData prev = getLastActiveStage(true);
905                prevProgressDeltaWasPositive = this.progress < progress;
906                
907                //progress += 30;
908                //progress = 40;
909                //progress = 40;
910                //progress = 499;
911                
912                this.progress = progress;
913                
914                
915                if (progress < 0) {
916                        progress = 0;
917                }
918                if (progress > getMaxProgress()) {
919                        progress = getMaxProgress();
920                }
921                
922                // Check to see if randomized events need to be rolled/reset
923                for (EventStageData esd : getStages()) {
924                        if (esd.wasEverReached && esd.isOneOffEvent && !esd.isRepeatable) continue;
925                        
926                        if (esd.randomized) {
927                                if ((esd.rollData != null && !esd.rollData.equals(RANDOM_EVENT_NONE)) && progress <= esd.progressToResetAt) {
928                                        resetRandomizedStage(esd);
929                                }
930                                if ((esd.rollData == null || esd.rollData.equals(RANDOM_EVENT_NONE)) && progress >= esd.progressToRollAt) {
931                                        rollRandomizedStage(esd);
932                                        if (esd.rollData == null) {
933                                                esd.rollData = RANDOM_EVENT_NONE;
934                                        }
935                                }
936                        }
937                }
938                
939                // go through all of the stages made active by the new progress value
940                // generally this'd just be one stage, but possible to have multiple for a large
941                // progress increase
942                for (EventStageData curr : getStages()) {
943                        if (curr.progress <= prev.progress && !prev.wasEverReached &&
944                                        (prev.rollData == null || prev.rollData.equals(RANDOM_EVENT_NONE))) continue;
945                        //if (curr.progress > progress) continue;
946                        
947                        // reached
948                        if (curr.progress <= progress) {
949                                boolean laterThanPrev = prev == null || ((Enum)prev.id).ordinal() < ((Enum)curr.id).ordinal();
950                                if (curr != null && (laterThanPrev || !prev.wasEverReached)) {
951                                        if (curr.sendIntelUpdateOnReaching && curr.progress > 0 && (prev == null || prev.progress < curr.progress)) {
952                                                sendUpdateIfPlayerHasIntel(curr, getTextPanelForStageChange());
953                                        }
954                                        notifyStageReached(curr);
955                                        curr.rollData = null;
956                                        curr.wasEverReached = true;
957                                        
958                                        progress = getProgress(); // in case it was changed by notifyStageReached()
959                                }
960                        }
961                }
962        }
963        
964        protected String getSoundForStageReachedUpdate(Object stageId) {
965                return getSoundMajorPosting();
966        }
967        protected String getSoundForOneTimeFactorUpdate(EventFactor factor) {
968                return null;
969        }
970        protected String getSoundForOtherUpdate(Object param) {
971                return null;
972        }
973        
974        public String getCommMessageSound() {
975                if (isSendingUpdate()) {
976                        if (getListInfoParam() instanceof EventStageData) {
977                                EventStageData esd = (EventStageData) getListInfoParam();
978                                String sound = getSoundForStageReachedUpdate(esd.id);
979                                if (sound != null) {
980                                        return sound;
981                                }
982                        } else if (getListInfoParam() instanceof EventFactor) {
983                                String sound = getSoundForOneTimeFactorUpdate((EventFactor) getListInfoParam());
984                                if (sound != null) {
985                                        return sound;
986                                }
987                        } else if (getListInfoParam() != null) {
988                                String sound = getSoundForOtherUpdate(getListInfoParam());
989                                if (sound != null) {
990                                        return sound;
991                                }
992                        }
993                        return getSoundStandardUpdate();
994                }
995                return getSoundStandardPosting();
996        }
997
998        protected void notifyStageReached(EventStageData stage) {
999                
1000        }
1001        
1002        public void reportEconomyMonthEnd() {
1003                
1004        }
1005        
1006        public Color getProgressColor(int delta) {
1007                if (isEventProgressANegativeThingForThePlayer()) {
1008                        if (delta < 0) {
1009                                return Misc.getPositiveHighlightColor();
1010                        }
1011                } else {
1012                        if (delta < 0) {
1013                                return Misc.getNegativeHighlightColor();
1014                        }
1015                }
1016                return Misc.getHighlightColor();
1017        }
1018        
1019        public void setHideStageWhenPastIt(Object stageId) {
1020                EventStageData esd = getDataFor(stageId);
1021                if (esd == null) return;
1022                esd.hideIconWhenPastStageUnlessLastActive = true;
1023        }
1024        
1025        public void setRandomized(Object stageId, RandomizedStageType type, int resetAt, int rollAt, boolean sendUpdateWhenReached) {
1026                setRandomized(stageId, type, resetAt, rollAt, sendUpdateWhenReached, true);
1027        }
1028        public void setRandomized(Object stageId, RandomizedStageType type, int resetAt, int rollAt, boolean sendUpdateWhenReached, boolean repeatable) {
1029                EventStageData esd = getDataFor(stageId);
1030                if (esd == null) return;
1031                esd.sendIntelUpdateOnReaching = sendUpdateWhenReached;
1032                esd.isRepeatable = repeatable;
1033                esd.isOneOffEvent = true;
1034                esd.randomized = true;
1035                esd.rollData = null;
1036                esd.progressToResetAt = resetAt;
1037                esd.progressToRollAt = rollAt;
1038                esd.randomType = type;
1039        }
1040
1041        public Random getRandom() {
1042                return random;
1043        }
1044        
1045        public void setRandom(Random random) {
1046                this.random = random;
1047        }
1048
1049        public void resetRandomizedStage(EventStageData stage) {
1050                stage.rollData = null;
1051        }
1052        
1053        public void rollRandomizedStage(EventStageData stage) {
1054                
1055        }
1056
1057        
1058        public float getProgressFraction() {
1059                float p = (float) progress / (float) maxProgress;
1060                if (p < 0) p = 0;
1061                if (p > 1) p = 1;
1062                return p;
1063        }
1064        
1065        public boolean withMonthlyFactors() {
1066                return true;
1067        }
1068        public boolean withOneTimeFactors() {
1069                return true;
1070        }
1071}
1072
1073
1074
1075
1076
1077
1078
1079
1080