001package com.fs.starfarer.api.impl.campaign.intel.bases;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.Iterator;
007import java.util.List;
008import java.util.Random;
009import java.util.Set;
010
011import java.awt.Color;
012
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.SectorEntityToken;
019import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
020import com.fs.starfarer.api.campaign.econ.Industry;
021import com.fs.starfarer.api.campaign.econ.MarketAPI;
022import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
023import com.fs.starfarer.api.impl.campaign.DebugFlags;
024import com.fs.starfarer.api.impl.campaign.econ.RecentUnrest;
025import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI;
026import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.EconomyRouteData;
027import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetRouteManager;
028import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator;
029import com.fs.starfarer.api.impl.campaign.fleets.RouteManager;
030import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
031import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
032import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
033import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
034import com.fs.starfarer.api.impl.campaign.ids.Conditions;
035import com.fs.starfarer.api.impl.campaign.ids.Factions;
036import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
037import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
038import com.fs.starfarer.api.impl.campaign.ids.Tags;
039import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
040import com.fs.starfarer.api.impl.campaign.intel.events.LuddicPathHostileActivityFactor;
041import com.fs.starfarer.api.impl.campaign.rulecmd.HA_CMD;
042import com.fs.starfarer.api.ui.Alignment;
043import com.fs.starfarer.api.ui.SectorMapAPI;
044import com.fs.starfarer.api.ui.TooltipMakerAPI;
045import com.fs.starfarer.api.util.IntervalUtil;
046import com.fs.starfarer.api.util.Misc;
047import com.fs.starfarer.api.util.WeightedRandomPicker;
048
049public class LuddicPathCellsIntel extends BaseIntelPlugin implements RouteFleetSpawner, FleetEventListener {
050
051        public static String USED_PLANETBUSTER_KEY = "$core_lpUsedPlanetbuster";
052        
053        public static float INCIDENT_PROB = Global.getSettings().getFloat("luddicPathCellsIncidentProbabilityPerMonth");
054        public static float MIN_WARNING_DAYS = Global.getSettings().getFloat("luddicPathCellsIncidentWarningMinDays");
055        
056//      public static float DISRUPTION_MIN = 90;
057//      public static float DISRUPTION_RANGE  = 60;
058        
059        public static float MIN_SABOTAGE = Global.getSettings().getFloatFromArray("luddicPathSabotageDays", 0); 
060        public static float MAX_SABOTAGE = Global.getSettings().getFloatFromArray("luddicPathSabotageDays", 1); 
061        
062        
063        public static Object UPDATE_DISSOLVED = new Object();
064        public static Object UPDATE_DISRUPTED = new Object();
065        
066        public static Object INCIDENT_PREP = new Object();
067        public static Object INCIDENT_PREVENTED = new Object();
068        public static Object INCIDENT_HAPPENED = new Object();
069        
070        
071        public static enum IncidentType {
072                REDUCED_STABILITY,
073                INDUSTRY_SABOTAGE,
074                PLANETBUSTER,
075        }
076        
077        protected boolean sleeper = false;
078        protected float sleeperTimeout = 0f;
079        
080        protected MarketAPI market;
081        
082        protected IntervalUtil incidentTracker = new IntervalUtil(20f, 40f);
083        protected Random random = new Random();
084        
085        protected int numIncidentAttempts = 0;
086        
087        protected float incidentDelay = 0f;
088        protected IncidentType incidentType = null;
089        protected RouteData smuggler = null;
090        
091        protected IncidentType prevIncident = null;
092        protected boolean prevIncidentSucceeded = false;
093        protected float sincePrevIncident = 0f;
094        protected Object prevIncidentData = null;
095        
096        protected float inertiaTime = 0f; // time the cell has existed despite not being a priority for the LP to maintain
097        
098        
099        public LuddicPathCellsIntel(MarketAPI market, boolean sleeper) {
100                this.market = market;
101                this.sleeper = sleeper;
102                
103                if (!market.isPlayerOwned()) {
104                        setPostingLocation(market.getPrimaryEntity());
105                }
106                
107                if (!market.hasCondition(Conditions.PATHER_CELLS)) {
108                        market.addCondition(Conditions.PATHER_CELLS, this);
109                }
110                
111                Global.getSector().addScript(this);
112                
113                if (market.isPlayerOwned() || DebugFlags.PATHER_BASE_DEBUG) {
114                        Global.getSector().getIntelManager().addIntel(this);
115                } else {
116                        Global.getSector().getIntelManager().queueIntel(this);
117                }
118        }
119        
120        public static LuddicPathBaseIntel getClosestBase(MarketAPI market) {
121                List<IntelInfoPlugin> bases = Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class);
122                float minDist = Float.MAX_VALUE;
123                LuddicPathBaseIntel closest = null;
124                for (IntelInfoPlugin curr : bases) {
125                        LuddicPathBaseIntel intel = (LuddicPathBaseIntel) curr;
126                        float dist = Misc.getDistance(intel.getMarket().getLocationInHyperspace(), market.getLocationInHyperspace());
127                        if (dist < minDist) {
128                                minDist = dist;
129                                closest = intel;
130                        }
131                }
132                return closest;
133        }
134        
135        public static List<LuddicPathCellsIntel> getCellsForBase(LuddicPathBaseIntel base, boolean includeSleeper) {
136                List<LuddicPathCellsIntel> result = new ArrayList<LuddicPathCellsIntel>();
137                
138                List<IntelInfoPlugin> cells = Global.getSector().getIntelManager().getIntel(LuddicPathCellsIntel.class);
139                for (IntelInfoPlugin curr : cells) {
140                        LuddicPathCellsIntel intel = (LuddicPathCellsIntel) curr;
141                        if (!includeSleeper && intel.isSleeper()) continue;
142                        if (getClosestBase(intel.getMarket()) == base) {
143                                result.add(intel);
144                        }
145                }
146                return result;
147        }
148        
149        public static LuddicPathCellsIntel getCellsForMarket(MarketAPI market) {
150                if (market == null) return null;
151                List<IntelInfoPlugin> cells = Global.getSector().getIntelManager().getIntel(LuddicPathCellsIntel.class);
152                for (IntelInfoPlugin curr : cells) {
153                        LuddicPathCellsIntel intel = (LuddicPathCellsIntel) curr;
154                        if (intel.getMarket() == market) return intel;
155                }
156                return null;
157        }
158        
159        
160        public MarketAPI getMarket() {
161                return market;
162        }
163
164        @Override
165        public boolean canMakeVisibleToPlayer(boolean playerInRelayRange) {
166                return super.canMakeVisibleToPlayer(playerInRelayRange);
167        }
168
169        @Override
170        protected void notifyEnded() {
171                super.notifyEnded();
172                Global.getSector().removeScript(this);
173        }
174
175
176        @Override
177        protected void notifyEnding() {
178                super.notifyEnding();
179                
180                if (market.hasCondition(Conditions.PATHER_CELLS)) {
181                        market.removeCondition(Conditions.PATHER_CELLS);
182                }
183        }
184        
185        
186        public void makeSleeper() {
187                makeSleeper(-1);
188        }
189        public void makeSleeper(float sleeperTimeout) {
190                if (sleeperTimeout >= 0) {
191                        this.sleeperTimeout = sleeperTimeout;
192                }
193                sleeper = true;
194        }
195        public void makeActiveIfPossible() {
196                if (sleeperTimeout <= 0) {
197                        sleeper = false;
198                }
199        }
200        
201
202        @Override
203        protected void advanceImpl(float amount) {
204                super.advanceImpl(amount);
205                
206                float days = Misc.getDays(amount);
207                
208                inertiaTime += days;
209                
210                if (sleeperTimeout > 0) {
211                        sleeperTimeout -= days;
212                        if (sleeperTimeout < 0) {
213                                sleeperTimeout = 0;
214                        }
215                }
216                
217                if (!market.isInEconomy()) {
218                        endImmediately();
219                        return;
220                }
221                
222                if (isSleeper()) return;
223                
224                // incidents handled through HostileActivityEventIntel now
225                // not anymore since the change from Hostile Activity -> Colony Crises
226                //if (market.isPlayerOwned()) return;
227                
228                if (DebugFlags.PATHER_BASE_DEBUG) {
229                        days *= 200f;
230                }
231                
232                if (prevIncident != null) {
233                        float mult = 1f;
234                        if (DebugFlags.PATHER_BASE_DEBUG) {
235                                mult = 1f / 20f;
236                        }
237                        sincePrevIncident += days * mult;
238                        if (sincePrevIncident >= 180f) {
239                                sincePrevIncident = 0f;
240                                prevIncident = null;
241                                prevIncidentData = null;
242                                prevIncidentSucceeded = false;
243                        }
244                }
245//              if (market.isPlayerOwned()) {
246//                      System.out.println("wefwefwe");
247//              }
248                if (incidentType == null && prevIncident == null) {
249                        incidentTracker.advance(days);
250                        if (incidentTracker.intervalElapsed() && random.nextFloat() < INCIDENT_PROB) {
251                                prepareIncident();
252                                numIncidentAttempts++;
253                        }
254                } else if (incidentType != null) {
255                        if (incidentDelay > 0 && smuggler == null) {
256                                incidentDelay -= days;
257                        }
258                        
259                        if (smuggler == null && incidentDelay <= 0) {
260                                beginIncident();
261                        }
262//                              if(market.isPlayerOwned()) {
263//                                      System.out.println("efwwefew");
264//                              }
265//                      smuggler.getActiveFleet().getLocation()
266//                      smuggler.getActiveFleet().getContainingLocation()
267                        if (smuggler != null) {
268                                RouteSegment segment = smuggler.getCurrent();
269                                if (segment != null && segment.getId() == EconomyFleetRouteManager.ROUTE_TRAVEL_SRC) {
270//                                      if(market.isPlayerOwned()) {
271//                                              System.out.println("efwwefew");
272//                                      }
273                                        doIncident();
274                                }
275                        }
276                }
277        }
278        
279        
280        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
281                
282                Color h = Misc.getHighlightColor();
283                Color g = Misc.getGrayColor();
284                float pad = 3f;
285                float opad = 10f;
286                
287                float initPad = pad;
288                if (mode == ListInfoMode.IN_DESC) initPad = opad;
289                
290                Color tc = getBulletColorForMode(mode);
291                
292                bullet(info);
293                boolean isUpdate = getListInfoParam() != null;
294                
295                if (mode != ListInfoMode.IN_DESC) {
296                        addMarketToList(info, market, initPad, tc);
297                        initPad = 0f;
298                }
299                //info.addPara(market., initPad)
300                
301                if (isUpdate) {
302                        if (getListInfoParam() == INCIDENT_HAPPENED) {
303                                if (!prevIncidentSucceeded) {
304                                        info.addPara("Incident averted by local security forces", initPad);
305                                } else {
306                                        switch (prevIncident) {
307                                        case INDUSTRY_SABOTAGE:
308                                                Industry ind = (Industry) prevIncidentData;
309                                                String days = getDays(ind.getDisruptedDays());
310                                                String daysStr = getDaysString(ind.getDisruptedDays());
311                                                info.addPara(ind.getCurrentName() + " operations disrupted for %s " + daysStr, initPad, tc, h, days);
312                                                break;
313                                        case REDUCED_STABILITY:
314                                                info.addPara("Stability reduced by %s", initPad, tc, h, "" + (Integer) prevIncidentData);
315                                                break;
316                                        case PLANETBUSTER:
317                                                info.addPara("Colony destroyed by planetbuster", initPad, tc);
318                                                break;
319                                        }
320                                }
321                        }
322                }
323                
324                unindent(info);
325        }
326        
327        @Override
328        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
329                Color c = getTitleColor(mode);
330                info.addPara(getName(), c, 0f);
331                addBulletPoints(info, mode);
332        }
333        
334        public void addInterestInfo(TooltipMakerAPI info, float width, float height) {
335                Color h = Misc.getHighlightColor();
336                float opad = 10f;
337                
338                info.addSectionHeading("Pather interest", getFactionForUIColors().getBaseUIColor(),
339                                  getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad);
340                
341                info.addPara("The following activity is attracting Pather interest, whether due to AI core use or the inherent nature of the industry:", opad);
342                
343                List<Industry> industries = new ArrayList<Industry>(market.getIndustries());
344                Iterator<Industry> iter = industries.iterator();
345                while (iter.hasNext()) {
346                        if (iter.next().isHidden()) {
347                                iter.remove();
348                        }
349                }
350                Collections.sort(industries, new Comparator<Industry>() {
351                        public int compare(Industry o1, Industry o2) {
352                                float s1 = o1.getPatherInterest();
353                                float s2 = o2.getPatherInterest();
354                                return (int) Math.signum(s2 - s1);
355                        }
356                });
357                String indent = "    ";
358                float initPad = 5f;
359                boolean added = false;
360                
361                String aiCoreId = market.getAdmin().getAICoreId();
362                if (aiCoreId != null) {
363                        int s = (int) Math.round(LuddicPathBaseManager.AI_CORE_ADMIN_INTEREST);
364                        if (market.getAdmin().getMemoryWithoutUpdate().getBoolean(MemFlags.SUSPECTED_AI)) {
365                                info.addPara(indent + "Suspected AI core administrator (%s)", initPad, h, "" + s);
366                        } else {
367                                info.addPara(indent + "AI core administrator (%s)", initPad, h, "" + s);
368                        }
369                        initPad = 3f;
370                        added = true;
371                }
372                
373                for (Industry ind : industries) {
374                        //float score = LuddicPathBaseManager.getLuddicPathMarketInterest(market);
375                        float score = ind.getPatherInterest();
376                        if ((int)Math.round(score) != 0) {
377                                int s = (int) Math.round(score);
378                                info.addPara(indent + ind.getCurrentName() + " (%s)", initPad, h, "" + s);
379                                initPad = 3f;
380                                added = true;
381                        }
382                }
383                
384                
385                
386                if (!added) {
387                        info.addPara(indent + "None", initPad);
388                }
389                
390                if (market.isPlayerOwned() && LuddicPathHostileActivityFactor.isPlayerDefeatedPatherExpedition()) {
391                        info.addPara("You defeated a Luddic Path armada sent against you, and " 
392                                        + "they are now more reluctant to take active measures against your colonies.", opad);
393                }
394                
395        }
396        
397        
398        @Override
399        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
400                Color h = Misc.getHighlightColor();
401                Color g = Misc.getGrayColor();
402                Color tc = Misc.getTextColor();
403                float pad = 3f;
404                float opad = 10f;
405                
406                if (width > 0) { // it's 0 when called from market condition tooltip
407                        info.addImage(getFactionForUIColors().getLogo(), width, 128, opad);
408                }
409                
410                if (isEnding()) {
411                        info.addPara("The Pather cells " +
412                                        market.getOnOrAt() + " " + market.getName() + " have been dissolved.", opad);
413                } else if (isSleeper() && sleeperTimeout <= 0) {
414                        info.addPara("There are indications that sleeper Luddic Path cells are being organized " +
415                                        market.getOnOrAt() + " " + market.getName() + ".", opad);
416                        info.addPara("The Pathers have not made any significant moves, but are apparently preparing " + 
417                                        "to do so if whatever activity they object to - industrial development, or the suspected " +
418                                        "use of AI cores, and other such - continues.", opad);
419                } else {
420                        info.addPara("There are active Luddic Path cells " +
421                                        market.getOnOrAt() + " " + market.getName() + ".", opad);
422//                      info.addPara("They are engaged in planning acts of terror and industrial sabotage, but " +
423//                                      "need material support - smuggled in from the nearest Pather base - to carry them off."
424//                                      + " The cells also provide intel to Pather fleets operating in-system.", opad);
425                        info.addPara("They are engaged in planning acts of terror and industrial sabotage, but " +
426                                        "are unlikely to carry them off unless the overal level of hostile activity in the system "
427                                        + "provides sufficient cover."
428                                        + " The cells also provide intel to Pather fleets operating in-system.", opad);
429                        
430                        if (sleeperTimeout > 0) {
431                                int daysNum = (int) Math.round(sleeperTimeout);
432                                if (daysNum < 1) daysNum = 1;
433                                String days = getDaysString(daysNum);
434                                info.addPara("However, the base supporting these cells is no longer operational. " +
435                                                "It is projected that establishing a new support network will take at least " +
436                                                "%s " + days + ", provided another base exists.", opad,
437                                                h,
438                                                "" + daysNum);
439                        } else {
440                                LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(market);
441                                if (base != null) {
442                                        if (base.isPlayerVisible()) {
443                                                info.addPara("The Pather base at the " + base.getMarket().getStarSystem().getNameWithLowercaseType() + 
444                                                        " is providing support to these cells.", opad);
445                                        } else {
446                                                info.addPara("You do not know the location of the Pather base providing support to these cells.", opad);
447                                        }
448                                        
449                                        info.addPara("If the base is destroyed, it will take some time to organize " +
450                                                        "support from another base, and both ground and fleet operations will be disrupted.", opad);
451                                } 
452                        }
453                }
454                
455                if (!isEnding()) {
456                        info.addSectionHeading("Impact", getFactionForUIColors().getBaseUIColor(),
457                                                                  getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad);
458                        
459                        if (!isSleeper()) {
460                                float stability = LuddicPathCells.STABLITY_PENALTY;
461//                              info.addPara("%s stability. Possibility of various acts of terror and sabotage, " +
462//                                              "if smugglers from a Luddic Path base are able to provide material support.", 
463//                                              opad, h,
464//                                              "-" + (int)stability);
465                                info.addPara("%s stability.", 
466                                                opad, h,
467                                                "-" + (int)stability);
468                        } else {
469                                if (sleeperTimeout <= 0) { // only show for actual sleeper cells, not "disrupted" active cells
470                                        info.addPara("No perceptible impact on operations as of yet.", opad);
471                                } else {
472                                        info.addPara("No impact on operations due to lack of material support.", opad);
473                                }
474                        }
475                        
476                        //addInterestInfo(info, width, height);
477                }
478                
479                if (prevIncident != null || incidentType == IncidentType.PLANETBUSTER) {
480                        info.addSectionHeading("Recent events", getFactionForUIColors().getBaseUIColor(),
481                                          getFactionForUIColors().getDarkUIColor(), Alignment.MID, opad);
482                        
483                        if (prevIncidentSucceeded) {
484                                if (incidentType == IncidentType.PLANETBUSTER) {
485                                        info.addPara("There are indications that the Pather cells are preparing to sneak " +
486                                                        "a planetbuster onto " + market.getName() + ". " + 
487                                                        "If they succeed, the colony will effectively be destroyed.", opad);
488                                } else if (prevIncident != null) {
489                                        switch (prevIncident) {
490                                        case INDUSTRY_SABOTAGE:
491                                                if (prevIncidentData instanceof Industry) {
492                                                        Industry ind = (Industry) prevIncidentData;
493                                                        if (ind.getDisruptedDays() > 2) {
494                                                                String days = getDays(ind.getDisruptedDays());
495                                                                String daysStr = getDaysString(ind.getDisruptedDays());
496                                                                info.addPara("The Pather cells have conducted a successful act of sabotage, " +
497                                                                                "disrupting " + ind.getCurrentName() + " operations for %s " + daysStr + ".", 
498                                                                                opad, h, days);
499                                                        }
500                                                }
501                                                break;
502                                        case REDUCED_STABILITY:
503                                                info.addPara("The Pather cells have conducted low-level attacks on various " +
504                                                                "industrial, military, and civilian targets, reducing stability by %s.",
505                                                                opad, h, "" + (Integer) prevIncidentData);
506                                                break;
507                                        case PLANETBUSTER:
508                                                info.addPara("The Pather cells have smuggled a planetbuster onto " + market.getName() + 
509                                                                " and detonated it. The colony has been effectively destroyed.", opad);
510                                                break;
511                                        }
512                                }
513                        } else {
514                                if (prevIncident != null) {
515                                        switch (prevIncident) {
516                                        case INDUSTRY_SABOTAGE:
517                                                if (prevIncidentData instanceof Industry) {
518                                                        Industry ind = (Industry) prevIncidentData;
519                                                        info.addPara("An attempted act of sabotage against " +
520                                                                                ind.getCurrentName() + " operations was averted by the local security forces.", 
521                                                                                opad);
522                                                }
523                                                break;
524                                        case REDUCED_STABILITY:
525                                                info.addPara("Multiple planned attacks against various industrial, " +
526                                                                "military, and civilian targets " +
527                                                                " were averted by the local security forces.", 
528                                                                opad);
529                                                break;
530                                        case PLANETBUSTER:
531                                                info.addPara("The Pather cells have smuggled a planetbuster onto " + market.getName() + 
532                                                                ", but the local security forces were able to locate and disarm it, thereby " +
533                                                                "saving the colony.", opad);
534                                                break;
535                                        }
536                                }
537                        }
538                        
539                        addBulletPoints(info, ListInfoMode.IN_DESC);
540                }
541                
542                if (!isEnding()) {
543                        addInterestInfo(info, width, height);
544                }
545        }
546        
547        public List<ArrowData> getArrowData(SectorMapAPI map) {
548                if (sleeperTimeout > 0) return null;
549                
550                LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(market);
551                if (base == null || !base.isPlayerVisible()) return null;
552                
553                List<ArrowData> result = new ArrayList<ArrowData>();
554                
555                
556                SectorEntityToken entityFrom = base.getMapLocation(map);
557                if (map != null) {
558                        SectorEntityToken iconEntity = map.getIntelIconEntity(base);
559                        if (iconEntity != null) {
560                                entityFrom = iconEntity;
561                        }
562                }
563                
564                ArrowData arrow = new ArrowData(entityFrom, market.getPrimaryEntity());
565                arrow.color = getFactionForUIColors().getBaseUIColor();
566                result.add(arrow);
567                
568                return result;
569        }
570        
571        @Override
572        public String getIcon() {
573                if (isSleeper()) {
574                        return Global.getSettings().getSpriteName("intel", "sleeper_cells");
575                }
576                return Global.getSettings().getSpriteName("intel", "active_cells");
577        }
578        
579        @Override
580        public Set<String> getIntelTags(SectorMapAPI map) {
581                Set<String> tags = super.getIntelTags(map);
582                tags.add(Factions.LUDDIC_PATH);
583                
584                if (market.isPlayerOwned() && !isSleeper()) {
585                        tags.add(Tags.INTEL_COLONIES);
586                }
587                
588                return tags;
589        }
590        
591        public String getSortString() {
592                String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix());
593                if (sleeper) {
594                        return base + " D"; // so it goes after "Luddic Path Base"
595                }
596                return base + " C"; // so it goes after "Luddic Path Base"
597        }
598        
599        public String getName() {
600                String base = "Luddic Path Cells";
601                
602                if (isSendingUpdate()) {
603                        if (getListInfoParam() == INCIDENT_HAPPENED) {
604                                if (prevIncidentSucceeded) {
605                                        return base + " - Incident";
606                                } else {
607                                        return base + " - Incident Averted";
608                                }
609                        }
610                }
611                
612                if (isEnding()) {
613                        return base + " - Dissolved";
614                }
615                if (sleeperTimeout > 0) {
616                        return base + " - Disrupted";
617                }
618                if (isSleeper()) {
619                        return base + " - Sleeper";
620                } else {
621                        return base + " - Active";
622                }
623        }
624        
625        @Override
626        public FactionAPI getFactionForUIColors() {
627                return Global.getSector().getFaction(Factions.LUDDIC_PATH);
628        }
629
630        public String getSmallDescriptionTitle() {
631                return getName();
632        }
633
634        @Override
635        public SectorEntityToken getMapLocation(SectorMapAPI map) {
636                return market.getPrimaryEntity();
637        }
638
639        
640        @Override
641        public String getCommMessageSound() {
642                return super.getCommMessageSound();
643        }
644
645        public boolean isSleeper() {
646                if (Factions.PLAYER.equals(market.getFactionId())) {
647                        if (HA_CMD.playerHasPatherAgreement()) {
648                                return true;
649                        }
650                }
651                return sleeper;
652        }
653
654        public void setSleeper(boolean sleeper) {
655                this.sleeper = sleeper;
656        }
657
658        public float getSleeperTimeout() {
659                return sleeperTimeout;
660        }
661
662        public void setSleeperTimeout(float sleeperTimeout) {
663                this.sleeperTimeout = sleeperTimeout;
664        }
665
666        
667        public String getRouteSourceId() {
668                return EconomyFleetRouteManager.SOURCE_ID;
669                //return "pather_cells_smuggler";
670        }
671        
672        
673        public void prepareIncident() {
674                abortIncident();
675                //if (incidentType != null) return;
676                LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(getMarket());
677                if (base == null) return;
678                
679                WeightedRandomPicker<IncidentType> types = new WeightedRandomPicker<IncidentType>(random);
680                
681                types.add(IncidentType.REDUCED_STABILITY, 10f);
682                if (numIncidentAttempts >= 3 || !market.isPlayerOwned()) {
683                        types.add(IncidentType.INDUSTRY_SABOTAGE, 10f);
684                }
685                
686//              if (numIncidentAttempts >= 10 && market.getSize() >= 5 && 
687//                              !Global.getSector().getMemory().is(USED_PLANETBUSTER_KEY, true)) {
688//                      types.add(IncidentType.PLANETBUSTER, 5f);
689//              }
690                
691                incidentType = types.pick();
692                //incidentType = IncidentType.REDUCED_STABILITY;
693                incidentDelay = MIN_WARNING_DAYS + random.nextFloat() * MIN_WARNING_DAYS;
694                
695                
696                if (incidentType == IncidentType.PLANETBUSTER) {
697                        incidentDelay = MIN_WARNING_DAYS * 4f + random.nextFloat() * 30f;
698                        Global.getSector().getMemoryWithoutUpdate().set(USED_PLANETBUSTER_KEY, true, 1500f);
699                }
700                
701//              if (market.isPlayerOwned()) {
702//                      sendUpdateIfPlayerHasIntel(INCIDENT_PREP, false);
703//              }
704        }
705        
706        public void beginIncident() {
707                LuddicPathBaseIntel base = LuddicPathCellsIntel.getClosestBase(getMarket());
708                if (base == null) {
709                        abortIncident();
710                        return;
711                }
712                
713                sendSmuggler(base);
714        }
715        
716        public void abortIncident() {
717                incidentDelay = 0;
718                incidentType = null;
719                if (smuggler != null && smuggler.getActiveFleet() != null) {
720                        smuggler.getActiveFleet().removeEventListener(this);
721                }
722                smuggler = null;
723        }
724        
725        protected boolean checkSuccess() {
726                float pSuccess = 1f - market.getStabilityValue() * 0.075f;
727                return random.nextFloat() < pSuccess;
728        }
729        
730        public void doIncident() {
731                if (incidentType == null) return;
732                
733                prevIncidentData = null;
734                
735                boolean success = checkSuccess();
736                
737                if (incidentType == IncidentType.REDUCED_STABILITY) {
738                        if (success) {
739                                RecentUnrest.get(market).add(3, 
740                                                Misc.ucFirst(Global.getSector().getFaction(Factions.LUDDIC_PATH).getPersonNamePrefix()) + " sabotage");
741                                prevIncidentData = 3;
742                        }
743                } else if (incidentType == IncidentType.INDUSTRY_SABOTAGE) {
744                        WeightedRandomPicker<Industry> picker = new WeightedRandomPicker<Industry>(random);
745                        for (Industry ind : market.getIndustries()) {
746                                if (!ind.canBeDisrupted()) continue;
747                                picker.add(ind, ind.getPatherInterest());
748                        }
749                        Industry target = picker.pick();
750                        if (target == null) {
751                                abortIncident();
752                                return;
753                        }
754                        
755                        prevIncidentData = target;
756                        if (success) {
757                                float disruptionDur = MIN_SABOTAGE + random.nextFloat() * (MAX_SABOTAGE - MIN_SABOTAGE);
758                                target.setDisrupted(disruptionDur, true);
759                        }
760                } else if (incidentType == IncidentType.PLANETBUSTER) {
761                        // ??? turn into lava planet, remove market, etc
762                }
763                
764                prevIncident = incidentType;
765                sincePrevIncident = 0f;
766                prevIncidentSucceeded = success;
767                
768                
769                if (DebugFlags.SEND_UPDATES_WHEN_NO_COMM || Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay() 
770                                || market.isPlayerOwned()) {
771                        if (market.isPlayerOwned() || 
772                                        incidentType == IncidentType.INDUSTRY_SABOTAGE ||
773                                        incidentType == IncidentType.PLANETBUSTER) {
774                                sendUpdateIfPlayerHasIntel(INCIDENT_HAPPENED, false);
775                        }
776                }
777                
778                abortIncident();
779        }
780        
781        
782        protected void sendSmuggler(LuddicPathBaseIntel base) {
783                String sid = getRouteSourceId();
784                
785                SectorEntityToken from = base.getMarket().getPrimaryEntity();
786                SectorEntityToken to = getMarket().getPrimaryEntity();
787                
788                EconomyRouteData data = new EconomyRouteData();
789                data.from = base.getMarket();
790                data.to = market;
791                data.smuggling = true;
792                data.cargoCap = 400;
793                data.fuelCap = 200;
794                
795                OptionalFleetData extra = new OptionalFleetData(data.from);
796                extra.fleetType = FleetTypes.TRADE_SMUGGLER;
797                
798                RouteData route = RouteManager.getInstance().addRoute(sid, base.getMarket(), Misc.genRandomSeed(), extra, this, data);
799                extra.strength = 50f;
800                extra.strength = Misc.getAdjustedStrength(extra.strength, base.getMarket());
801                
802                
803                float orbitDays = 3f + random.nextFloat() * 3f;
804                float travelDays = RouteLocationCalculator.getTravelDays(from, to);
805                if (DebugFlags.PATHER_BASE_DEBUG) travelDays *= 0.1f;
806                
807                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_SRC_LOAD, orbitDays, from));
808                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_TRAVEL_DST, travelDays, from, to));
809                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_DST_UNLOAD, orbitDays * 0.5f, to));
810                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_DST_LOAD, orbitDays * 0.5f, to));
811                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_TRAVEL_SRC, travelDays, to, from));
812                route.addSegment(new RouteSegment(EconomyFleetRouteManager.ROUTE_SRC_UNLOAD, orbitDays, from));
813                
814                smuggler = route;
815        }
816        
817        public void reportAboutToBeDespawnedByRouteManager(RouteData route) {
818                
819        }
820
821        public boolean shouldCancelRouteAfterDelayCheck(RouteData route) {
822                return false;
823        }
824
825        public boolean shouldRepeat(RouteData route) {
826                return false;
827        }
828
829        public CampaignFleetAPI spawnFleet(RouteData route) {
830                Random random = new Random();
831                if (route.getSeed() != null) {
832                        random = new Random(route.getSeed());
833                }
834                
835                CampaignFleetAPI fleet = EconomyFleetRouteManager.createTradeRouteFleet(route, random);
836                if (fleet == null) return null;;
837                
838                fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_DO_NOT_IGNORE_PLAYER, true);
839                fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_IGNORES_OTHER_FLEETS, true);
840                fleet.addEventListener(this);
841                fleet.addScript(new EconomyFleetAssignmentAI(fleet, route));
842                return fleet;
843        }
844
845        public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) {
846                if (smuggler == null || smuggler.getActiveFleet() == null) return;
847                
848                CampaignFleetAPI active = smuggler.getActiveFleet();
849                if (!battle.isInvolved(active)) return;
850                
851                if (battle.getSideFor(active) != battle.getSideFor(primaryWinner)) {
852                        abortIncident();
853                }
854        }
855
856        public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
857                if (smuggler != null && fleet == smuggler.getActiveFleet()) {
858                        abortIncident();
859                }
860        }
861
862        public float getInertiaTime() {
863                return inertiaTime;
864        }
865
866        public void setInertiaTime(float inertiaTime) {
867                this.inertiaTime = inertiaTime;
868        }
869        
870        
871}
872
873
874
875
876
877
878