001package com.fs.starfarer.api.impl.campaign.intel.bases;
002
003import java.util.LinkedHashMap;
004import java.util.List;
005import java.util.Random;
006import java.util.Set;
007
008import java.awt.Color;
009
010import org.apache.log4j.Logger;
011import org.json.JSONException;
012import org.json.JSONObject;
013
014import com.fs.starfarer.api.EveryFrameScript;
015import com.fs.starfarer.api.Global;
016import com.fs.starfarer.api.campaign.BattleAPI;
017import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
018import com.fs.starfarer.api.campaign.CampaignFleetAPI;
019import com.fs.starfarer.api.campaign.FactionAPI;
020import com.fs.starfarer.api.campaign.PersonImportance;
021import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult;
022import com.fs.starfarer.api.campaign.SectorEntityToken;
023import com.fs.starfarer.api.campaign.StarSystemAPI;
024import com.fs.starfarer.api.campaign.TextPanelAPI;
025import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
026import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
027import com.fs.starfarer.api.campaign.econ.EconomyAPI.EconomyUpdateListener;
028import com.fs.starfarer.api.campaign.econ.Industry;
029import com.fs.starfarer.api.campaign.econ.MarketAPI;
030import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel;
031import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
032import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
033import com.fs.starfarer.api.characters.PersonAPI;
034import com.fs.starfarer.api.combat.MutableStat.StatMod;
035import com.fs.starfarer.api.fleet.FleetMemberAPI;
036import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin;
037import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact;
038import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
039import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
040import com.fs.starfarer.api.impl.campaign.DebugFlags;
041import com.fs.starfarer.api.impl.campaign.ids.Conditions;
042import com.fs.starfarer.api.impl.campaign.ids.Entities;
043import com.fs.starfarer.api.impl.campaign.ids.Factions;
044import com.fs.starfarer.api.impl.campaign.ids.Industries;
045import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
046import com.fs.starfarer.api.impl.campaign.ids.Ranks;
047import com.fs.starfarer.api.impl.campaign.ids.Stats;
048import com.fs.starfarer.api.impl.campaign.ids.Submarkets;
049import com.fs.starfarer.api.impl.campaign.ids.Tags;
050import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
051import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResult;
052import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResultType;
053import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarData;
054import com.fs.starfarer.api.impl.campaign.intel.bar.events.LuddicPathBaseBarEvent;
055import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel.BaseBountyData;
056import com.fs.starfarer.api.impl.campaign.intel.deciv.DecivTracker;
057import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel;
058import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidDelegate;
059import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidStageStatus;
060import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames;
061import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames.MarkovNameResult;
062import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator;
063import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity;
064import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.EntityLocation;
065import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.LocationType;
066import com.fs.starfarer.api.ui.Alignment;
067import com.fs.starfarer.api.ui.SectorMapAPI;
068import com.fs.starfarer.api.ui.TooltipMakerAPI;
069import com.fs.starfarer.api.util.IntervalUtil;
070import com.fs.starfarer.api.util.Misc;
071import com.fs.starfarer.api.util.WeightedRandomPicker;
072
073public class LuddicPathBaseIntel extends BaseIntelPlugin implements EveryFrameScript, FleetEventListener,
074                                                                                                                                EconomyUpdateListener, RaidDelegate {
075        
076        public static final String PATHER_BASE_COMMANDER = "$patherBaseCommander";
077        
078        public static String MEM_FLAG = "$core_luddicPathBase";
079        
080        public static Object BOUNTY_EXPIRED_PARAM = new Object();
081        public static Object DISCOVERED_PARAM = new Object();
082        
083        public static Logger log = Global.getLogger(LuddicPathBaseIntel.class);
084        
085        protected PersonAPI baseCommander;
086        protected StarSystemAPI system;
087        protected MarketAPI market;
088        protected SectorEntityToken entity;
089        
090        protected float elapsedDays = 0f;
091        protected float duration = 45f;
092        
093        protected BaseBountyData bountyData = null;
094        
095        protected IntervalUtil monthlyInterval = new IntervalUtil(20f, 40f);
096        protected int monthsNoBounty = 0;
097        
098        protected boolean large = false;
099        
100        protected Random random = new Random();
101        
102        public static LuddicPathBaseIntel getIntelFor(StarSystemAPI system) {
103                for (IntelInfoPlugin intel : Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class)) {
104                        if (((LuddicPathBaseIntel)intel).getSystem() == system) {
105                                return (LuddicPathBaseIntel) intel;
106                        }
107                }
108                return null;
109        }
110        
111        public static LuddicPathBaseIntel getIntelFor(MarketAPI market) {
112                for (IntelInfoPlugin p : Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class)) {
113                        LuddicPathBaseIntel intel = (LuddicPathBaseIntel) p;
114                        if (intel.getMarket() == market) return intel;
115                }
116                return null;
117        }
118        
119        public LuddicPathBaseIntel(StarSystemAPI system, String factionId) {
120                this.system = system;
121        
122                market = Global.getFactory().createMarket(Misc.genUID(), "Luddic Path Base", 3);
123                market.setSize(3);
124                market.setHidden(true);
125                market.getMemoryWithoutUpdate().set(MEM_FLAG, true);
126                market.getMemoryWithoutUpdate().set(MemFlags.HIDDEN_BASE_MEM_FLAG, true);
127                
128                market.setFactionId(Factions.LUDDIC_PATH);
129                
130                market.setSurveyLevel(SurveyLevel.FULL);
131                
132                market.setFactionId(factionId);
133                market.addCondition(Conditions.POPULATION_3);
134                
135                market.addIndustry(Industries.POPULATION);
136                market.addIndustry(Industries.SPACEPORT);
137                market.addIndustry(Industries.MILITARYBASE);
138                
139                market.addSubmarket(Submarkets.SUBMARKET_OPEN);
140                market.addSubmarket(Submarkets.SUBMARKET_BLACK);
141                
142                market.getTariff().modifyFlat("default_tariff", market.getFaction().getTariffFraction());
143                
144                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
145                weights.put(LocationType.IN_ASTEROID_BELT, 10f);
146                weights.put(LocationType.IN_ASTEROID_FIELD, 10f);
147                weights.put(LocationType.IN_RING, 10f);
148                weights.put(LocationType.IN_SMALL_NEBULA, 10f);
149                weights.put(LocationType.GAS_GIANT_ORBIT, 10f);
150                weights.put(LocationType.PLANET_ORBIT, 10f);
151                WeightedRandomPicker<EntityLocation> locs = BaseThemeGenerator.getLocations(null, system, null, 100f, weights);
152                if (locs.isEmpty()) {
153                        weights.clear();
154                        weights.put(LocationType.STAR_ORBIT, 10f);
155                        weights.put(LocationType.OUTER_SYSTEM, 0.0001f);
156                        locs = BaseThemeGenerator.getLocations(null, system, null, 100f, weights);
157                }
158                EntityLocation loc = locs.pick();
159                
160                if (loc == null) {
161                        endImmediately();
162                        return;
163                }
164                
165                AddedEntity added = BaseThemeGenerator.addNonSalvageEntity(system, loc, Entities.MAKESHIFT_STATION, factionId);
166                
167                if (added == null || added.entity == null) {
168                        endImmediately();
169                        return;
170                }
171                
172                entity = added.entity;
173                
174                
175                String name = generateName();
176                if (name == null) {
177                        endImmediately();
178                        return;
179                }
180                
181                market.setName(name);
182                entity.setName(name);
183                
184                BaseThemeGenerator.convertOrbitWithSpin(entity, -5f);
185                
186                market.setPrimaryEntity(entity);
187                entity.setMarket(market);
188                
189                entity.setSensorProfile(1f);
190                entity.setDiscoverable(true);
191                entity.getDetectedRangeMod().modifyFlat("gen", 5000f);
192                
193                market.setEconGroup(market.getId());
194                market.getMemoryWithoutUpdate().set(DecivTracker.NO_DECIV_KEY, true);
195                
196                market.reapplyIndustries();
197                
198                Global.getSector().getEconomy().addMarket(market, true);
199                
200                baseCommander = market.getFaction().createRandomPerson(Misc.random);
201                baseCommander.setRankId(Ranks.SPACE_CAPTAIN);
202                baseCommander.setPostId(Ranks.POST_STATION_COMMANDER);
203                baseCommander.setImportanceAndVoice(PersonImportance.HIGH, Misc.random);
204                baseCommander.addTag(Tags.CONTACT_MILITARY);
205                //baseCommander.addTag(Tags.CONTACT_PATHER); // currently no missions for that
206                baseCommander.getMemoryWithoutUpdate().set(PATHER_BASE_COMMANDER, true);
207                baseCommander.setImportance(PersonImportance.VERY_HIGH);
208                market.getCommDirectory().addPerson(baseCommander);
209                
210                Global.getSector().getIntelManager().addIntel(this, true);
211                if (!DebugFlags.PATHER_BASE_DEBUG) {
212                        timestamp = null;
213                }
214                
215                Global.getSector().getListenerManager().addListener(this);
216                Global.getSector().getEconomy().addUpdateListener(this);
217                
218                large = random.nextFloat() > 0.5f;
219                updateStationIfNeeded(large);
220                
221                PortsideBarData.getInstance().addEvent(new LuddicPathBaseBarEvent(this));
222                
223                log.info(String.format("Added luddic path base in [%s], isLarge: %s", system.getName(), "" + large));
224        }
225        
226        @Override
227        public boolean isHidden() {
228                //if (true) return false;
229                if (super.isHidden()) return true;
230                return timestamp == null;
231        }
232
233//      public float getRaidFP() {
234//              float base = getBaseRaidFP();
235//              return base * (0.75f + (float) Math.random() * 0.5f);
236//      }
237//      public float getBaseRaidFP() {
238//              float base = 100f;
239//              return base * (0.75f + (float) Math.random() * 0.5f);
240//      }
241//      
242        public void notifyRaidEnded(RaidIntel raid, RaidStageStatus status) {
243                if (status == RaidStageStatus.SUCCESS) {
244                } else {
245                }
246        }
247//      
248//      public void startRaid(StarSystemAPI target, float raidFP) {
249//              boolean hasTargets = false;
250//              for (MarketAPI curr : Misc.getMarketsInLocation(target)) {
251//                      if (curr.getFaction().isHostileTo(getFactionForUIColors())) {
252//                              hasTargets = true;
253//                              break;
254//                      }
255//              }
256//              
257//              if (!hasTargets) return;
258//              
259//              RaidIntel raid = new RaidIntel(target, getFactionForUIColors(), this);
260//              
261//              //float raidFP = 1000;
262//              float successMult = 0.75f;
263//              
264//              JumpPointAPI gather = null;
265//              List<JumpPointAPI> points = system.getEntities(JumpPointAPI.class);
266//              float min = Float.MAX_VALUE;
267//              for (JumpPointAPI curr : points) {
268//                      float dist = Misc.getDistance(entity.getLocation(), curr.getLocation());
269//                      if (dist < min) {
270//                              min = dist;
271//                              gather = curr;
272//                      }
273//              }
274//              
275//              
276//              PirateRaidAssembleStage assemble = new PirateRaidAssembleStage(raid, gather, this);
277//              assemble.addSource(market);
278//              assemble.setSpawnFP(raidFP);
279//              assemble.setAbortFP(raidFP * successMult);
280//              raid.addStage(assemble);
281//              
282//              
283//              SectorEntityToken raidJump = RouteLocationCalculator.findJumpPointToUse(getFactionForUIColors(), target.getCenter());
284//              
285//              TravelStage travel = new TravelStage(raid, gather, raidJump, false);
286//              travel.setAbortFP(raidFP * successMult * successMult);
287//              raid.addStage(travel);
288//              
289//              PirateRaidActionStage action = new PirateRaidActionStage(raid, target);
290//              action.setAbortFP(raidFP * successMult * successMult * successMult);
291//              raid.addStage(action);
292//              
293//              raid.addStage(new ReturnStage(raid));
294//              
295//              if (!Misc.getMarketsInLocation(target, Factions.PLAYER).isEmpty()) {
296//                      Global.getSector().getIntelManager().addIntel(raid);
297//              } else {
298//                      Global.getSector().getIntelManager().queueIntel(raid);
299//              }
300//      }
301        
302        public StarSystemAPI getSystem() {
303                return system;
304        }
305
306        protected String pickStationType(boolean large) {
307                WeightedRandomPicker<String> stations = new WeightedRandomPicker<String>();
308                
309                //large = true;
310                
311                try {
312                        JSONObject json = getFactionForUIColors().getCustom().getJSONObject(Factions.CUSTOM_PATHER_BASES_SMALL);
313                        if (large) json = getFactionForUIColors().getCustom().getJSONObject(Factions.CUSTOM_PATHER_BASES_LARGE);
314                                for (String key : JSONObject.getNames(json)) {
315                                        stations.add(key, (float) json.optDouble(key, 0f));
316                                }
317                        if (stations.isEmpty()) {
318                                stations.add(Industries.ORBITALSTATION, 5f);
319                        }
320                } catch (JSONException e) {
321                        stations.clear();
322                }
323                
324                return stations.pick();
325        }
326        
327        protected Industry getStationIndustry() {
328                for (Industry curr : market.getIndustries()) {
329                        if (curr.getSpec().hasTag(Industries.TAG_STATION)) {
330                                return curr;
331                        }
332                }
333                return null;
334        }
335        
336        protected void updateStationIfNeeded(boolean large) {
337                Industry stationInd = getStationIndustry();
338                
339                String currIndId = null;
340                if (stationInd != null) {
341                        currIndId = stationInd.getId();
342                        market.removeIndustry(stationInd.getId(), null, false);
343                        stationInd = null;
344                }
345                
346                if (currIndId == null) {
347                        currIndId = pickStationType(large);
348                }
349                
350                if (currIndId == null) return;
351                
352                market.addIndustry(currIndId);
353                stationInd = getStationIndustry();
354                if (stationInd == null) return;
355                
356                stationInd.finishBuildingOrUpgrading();
357                
358
359                CampaignFleetAPI fleet = Misc.getStationFleet(entity);
360                if (fleet == null) return;
361                
362                List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
363                if (members.size() < 1) return;
364                
365                fleet.inflateIfNeeded();
366        }
367        
368        
369        protected CampaignFleetAPI addedListenerTo = null;
370        @Override
371        protected void advanceImpl(float amount) {
372                float days = Global.getSector().getClock().convertToDays(amount);
373                //days *= 1000f;
374                //Global.getSector().getCurrentLocation().getName()
375                //entity.getContainingLocation().getName()
376                if (getPlayerVisibleTimestamp() == null && entity.isInCurrentLocation() && isHidden()) {
377                        makeKnown();
378                        sendUpdateIfPlayerHasIntel(DISCOVERED_PARAM, false);
379                }
380                
381                if (!sentBountyUpdate && bountyData != null && 
382                                (Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay() ||
383                                                (!isHidden() && DebugFlags.SEND_UPDATES_WHEN_NO_COMM))) {
384                        makeKnown();
385                        sendUpdateIfPlayerHasIntel(bountyData, false);
386                        sentBountyUpdate = true;
387                }
388                
389                CampaignFleetAPI fleet = Misc.getStationFleet(market);
390                if (fleet != null && addedListenerTo != fleet) {
391                        if (addedListenerTo != null) {
392                                addedListenerTo.removeEventListener(this);
393                        }
394                        fleet.addEventListener(this);
395                        addedListenerTo = fleet;
396                }
397                
398                monthlyInterval.advance(days);
399                if (monthlyInterval.intervalElapsed()) {
400                        if (bountyData == null && random.nextFloat() < Math.min(0.3f, monthsNoBounty * 0.02f)) {
401                                setBounty();
402                        } else {
403                                monthsNoBounty++;
404                        }
405                }
406
407//              if (bountyData == null) {
408//                      setBounty();
409//              }
410                
411                if (bountyData != null) {
412                        boolean canEndBounty = !entity.isInCurrentLocation();
413                        bountyData.bountyElapsedDays += days;
414                        if (bountyData.bountyElapsedDays > bountyData.bountyDuration && canEndBounty) {
415                                endBounty();
416                        }
417                }
418        }
419        
420        
421        public void makeKnown() {
422                makeKnown(null);
423        }
424        public void makeKnown(TextPanelAPI text) {
425//              entity.setDiscoverable(null);
426//              entity.setSensorProfile(null);
427//              entity.getDetectedRangeMod().unmodify("gen");
428                
429                if (getPlayerVisibleTimestamp() == null) {
430                        Global.getSector().getIntelManager().removeIntel(this);
431                        Global.getSector().getIntelManager().addIntel(this, text == null, text);
432                }
433        }
434        
435        public float getTimeRemainingFraction() {
436                float f = 1f - elapsedDays / duration;
437                return f;
438        }
439        
440        
441
442        @Override
443        protected void notifyEnding() {
444                super.notifyEnding();
445                log.info(String.format("Removing luddic path base at [%s]", system.getName()));
446                Global.getSector().getListenerManager().removeListener(this);
447                
448                Global.getSector().getEconomy().removeMarket(market);
449                Global.getSector().getEconomy().removeUpdateListener(this);
450                Misc.removeRadioChatter(market);
451                market.advance(0f);
452        }
453        
454        @Override
455        protected void notifyEnded() {
456                super.notifyEnded();
457        }
458
459
460
461        protected BountyResult result = null;
462        public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
463                if (isEnding()) return;
464                
465                //CampaignFleetAPI station = Misc.getStationFleet(market); // null here since it's the skeleton station at this point
466                if (addedListenerTo != null && fleet == addedListenerTo) {
467                        Misc.fadeAndExpire(entity);
468                        endAfterDelay();
469                        
470                        result = new BountyResult(BountyResultType.END_OTHER, 0, null);
471                        
472                        if (reason == FleetDespawnReason.DESTROYED_BY_BATTLE && 
473                                        param instanceof BattleAPI) {
474                                BattleAPI battle = (BattleAPI) param;
475                                if (battle.isPlayerInvolved()) {
476                                        int payment = 0;
477                                        if (bountyData != null) {
478                                                payment = (int) (bountyData.baseBounty * battle.getPlayerInvolvementFraction());
479                                        }
480                                        if (payment > 0) {
481                                                Global.getSector().getPlayerFleet().getCargo().getCredits().add(payment);
482                                                
483                                                CustomRepImpact impact = new CustomRepImpact();
484                                                impact.delta = bountyData.repChange * battle.getPlayerInvolvementFraction();
485                                                if (impact.delta < 0.01f) impact.delta = 0.01f;
486                                                ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation(
487                                                                new RepActionEnvelope(RepActions.CUSTOM, 
488                                                                                impact, null, null, false, true),
489                                                                                bountyData.bountyFaction.getId());
490                                                
491                                                result = new BountyResult(BountyResultType.END_PLAYER_BOUNTY, payment, rep);
492                                        } else {
493                                                result = new BountyResult(BountyResultType.END_PLAYER_NO_REWARD, 0, null);
494                                        }
495                                }
496                        }
497                        
498                        boolean sendUpdate = DebugFlags.SEND_UPDATES_WHEN_NO_COMM ||
499                                                                 result.type != BountyResultType.END_OTHER ||
500                                                                 Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay();
501                        sendUpdate = true;
502                        if (sendUpdate) {
503                                sendUpdateIfPlayerHasIntel(result, false);
504                        }
505                        
506                        for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, false)) {
507                                cell.makeSleeper(Global.getSettings().getFloat("patherCellDisruptionDuration"));
508                                if (cell.getMarket().isPlayerOwned() || DebugFlags.PATHER_BASE_DEBUG) {
509                                        cell.sendUpdateIfPlayerHasIntel(LuddicPathCellsIntel.UPDATE_DISRUPTED, false);
510                                }
511                                ListenerUtil.reportCellDisrupted(cell);
512                        }
513                        
514                        PirateBaseManager.markRecentlyUsedForBase(system);
515                        LuddicPathBaseManager.getInstance().incrDestroyed();
516                        
517                }
518        }
519
520        public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) {
521                
522        }
523        
524        public boolean runWhilePaused() {
525                return false;
526        }
527        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
528                
529                Color h = Misc.getHighlightColor();
530                Color g = Misc.getGrayColor();
531                float pad = 3f;
532                float opad = 10f;
533                
534                float initPad = pad;
535                if (mode == ListInfoMode.IN_DESC) initPad = opad;
536                
537                Color tc = getBulletColorForMode(mode);
538                
539                bullet(info);
540                boolean isUpdate = getListInfoParam() != null;
541                
542                
543                if (bountyData != null && result == null) {
544                        if (getListInfoParam() != BOUNTY_EXPIRED_PARAM) {
545                                if (isUpdate || mode != ListInfoMode.IN_DESC) {
546                                        FactionAPI faction = bountyData.bountyFaction;
547                                        info.addPara("Bounty faction: " + faction.getDisplayName(), initPad, tc,
548                                                        faction.getBaseUIColor(), faction.getDisplayName());
549                                        initPad = 0f;
550                                }
551                                info.addPara("%s reward", initPad, tc, h, Misc.getDGSCredits(bountyData.baseBounty));
552                                addDays(info, "remaining", bountyData.bountyDuration - bountyData.bountyElapsedDays, tc);
553                        }
554                }
555                
556                if (result != null && bountyData != null) {
557                        switch (result.type) {
558                        case END_PLAYER_BOUNTY:
559                                info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(result.payment));
560                                CoreReputationPlugin.addAdjustmentMessage(result.rep.delta, bountyData.bountyFaction, null, 
561                                                null, null, info, tc, isUpdate, 0f);
562                                break;
563                        case END_TIME:
564                                break;
565                        case END_OTHER:
566                                break;
567                        
568                        }
569                }
570
571                unindent(info);
572        }
573        
574        @Override
575        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
576                Color c = getTitleColor(mode);
577                info.addPara(getName(), c, 0f);
578                addBulletPoints(info, mode);
579        }
580        
581        public String getSortString() {
582                if (getTagsForSort().contains(Tags.INTEL_FLEET_LOG) || getTagsForSort().contains(Tags.INTEL_EXPLORATION)) {
583                        return getSortStringNewestFirst();
584                }
585                
586                String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix());
587                return base + " Base";
588        }
589        
590        public String getName() {
591                String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix());
592                
593                if (getListInfoParam() == bountyData && bountyData != null) {
594                        return base + " Base - Bounty Posted";
595                } else if (getListInfoParam() == BOUNTY_EXPIRED_PARAM) {
596                        return base + " Base - Bounty Expired";
597                }
598                
599                if (result != null) {
600                        if (result.type == BountyResultType.END_PLAYER_BOUNTY) {
601                                return base + " Base - Bounty Completed";
602                        } else if (result.type == BountyResultType.END_PLAYER_NO_REWARD) {
603                                return base + " Base - Destroyed";
604                        }
605                }
606                
607                String name = market.getName();
608                if (isEnding()) {
609                        //return "Base Abandoned - " + name;
610                        return base + " Base - Abandoned";
611                }
612                if (getListInfoParam() == DISCOVERED_PARAM) {
613                        return base + " Base - Discovered";
614                }
615                if (entity.isDiscoverable()) {
616                        return base + " Base - Exact Location Unknown";
617                }
618                return base + " Base - " + name;
619        }
620        
621        @Override
622        public FactionAPI getFactionForUIColors() {
623                return market.getFaction();
624        }
625
626        public String getSmallDescriptionTitle() {
627                return getName();
628        }
629        
630        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
631                
632                Color h = Misc.getHighlightColor();
633                Color g = Misc.getGrayColor();
634                Color tc = Misc.getTextColor();
635                float pad = 3f;
636                float opad = 10f;
637
638                FactionAPI faction = market.getFaction();
639                
640                info.addImage(faction.getLogo(), width, 128, opad);
641                
642                String has = faction.getDisplayNameHasOrHave();
643                
644                info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + has + 
645                                " established a base in the " + 
646                                market.getContainingLocation().getNameWithLowercaseType() + ". " +
647                                                "The base serves to provide material support to active Pather cells on nearby colonies, enabling them " +
648                                                "to cause widespread damage and destruction.",
649                                opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle());
650                
651                if (!entity.isDiscoverable()) {
652                        if (large) {
653                                info.addPara("It has extremely well-developed defensive capabilities " +
654                                                "and is protected by a large number of fleets.", opad);
655                        } else {
656                                info.addPara("It has well-developed defensive capabilities " +
657                                                "and is protected by a large number of fleets.", opad);
658                        }
659                } else {
660                        info.addPara("You have not yet discovered the exact location or capabilities of this base.", opad);
661                }
662                info.addSectionHeading("Recent events", 
663                                                           faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad);
664                
665                
666                List<LuddicPathCellsIntel> cells = LuddicPathCellsIntel.getCellsForBase(this, false);
667                if (!cells.isEmpty()) {
668                        float initPad = opad;
669                
670                        info.addPara("This base is known to be providing support to active Pather cells at the following colonies:", opad);
671                        for (LuddicPathCellsIntel intel : cells) {
672                                addMarketToList(info, intel.getMarket(), initPad, tc);
673                                initPad = 0f;
674                        }
675                        initPad = 0f;
676                } else {
677                        info.addPara("You do not know of any active pather cells this base might be providing support to.", opad);                      
678                }
679        
680                
681                if (bountyData != null) {
682                        info.addPara(Misc.ucFirst(bountyData.bountyFaction.getDisplayNameWithArticle()) + " " +
683                                        bountyData.bountyFaction.getDisplayNameHasOrHave() + 
684                                        " posted a bounty for the destruction of this base.",
685                                        opad, bountyData.bountyFaction.getBaseUIColor(), 
686                                        bountyData.bountyFaction.getDisplayNameWithArticleWithoutArticle());
687                        
688                        if (result != null && result.type == BountyResultType.END_PLAYER_BOUNTY) {
689                                info.addPara("You have successfully completed this bounty.", opad);
690                        }
691                        
692                        addBulletPoints(info, ListInfoMode.IN_DESC);
693                }
694                
695                if (result != null) {
696                        if (result.type == BountyResultType.END_PLAYER_NO_REWARD) {
697                                info.addPara("You have destroyed this base.", opad);                            
698                        } else if (result.type == BountyResultType.END_OTHER) {
699                                info.addPara("It is rumored that this base is no longer operational.", opad);                           
700                        }
701                }
702
703        }
704        
705        public String getIcon() {
706                return Global.getSettings().getSpriteName("intel", "pather_base");
707                //return market.getFaction().getCrest();
708        }
709        
710        public Set<String> getIntelTags(SectorMapAPI map) {
711                Set<String> tags = super.getIntelTags(map);
712                if (bountyData != null) {
713                        tags.add(Tags.INTEL_BOUNTY);
714                }
715                
716                tags.add(Tags.INTEL_EXPLORATION);
717                
718//              if (target != null && !Misc.getMarketsInLocation(target, Factions.PLAYER).isEmpty()) {
719//                      tags.add(Tags.INTEL_COLONIES);
720//              }
721                
722                for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, true)) {
723                        if (cell.getMarket().isPlayerOwned() && !cell.isSleeper()) {
724                                tags.add(Tags.INTEL_COLONIES);
725                                break;
726                        }
727                }
728                
729                tags.add(market.getFactionId());
730                if (bountyData != null) {
731                        tags.add(bountyData.bountyFaction.getId());
732                }
733                return tags;
734        }
735
736        @Override
737        public SectorEntityToken getMapLocation(SectorMapAPI map) {
738                if (market.getPrimaryEntity().isDiscoverable()) {
739                        return system.getCenter();
740                }
741                return market.getPrimaryEntity();
742        }
743        
744        
745        
746        protected String generateName() {
747                MarkovNames.loadIfNeeded();
748                
749                MarkovNameResult gen = null;
750                for (int i = 0; i < 10; i++) {
751                        gen = MarkovNames.generate(null);
752                        if (gen != null) {
753                                String test = gen.name;
754                                if (test.toLowerCase().startsWith("the ")) continue;
755                                String p = pickPostfix();
756                                if (p != null && !p.isEmpty()) {
757                                        test += " " + p;
758                                }
759                                if (test.length() > 22) continue;
760                                
761                                return test;
762                        }
763                }
764                return null;
765        }
766        
767        private String pickPostfix() {
768                WeightedRandomPicker<String> post = new WeightedRandomPicker<String>();
769                post.add("Asylum");
770                //post.add("Base"); -> otherwise intel title can look like this: "Luddic Path Base: Scrimshaw Base"
771                post.add("Citadel");
772                post.add("Hammer");
773                post.add("Harbor");
774                post.add("Haven");
775                post.add("Hold");
776                post.add("Locus");
777                post.add("Nexus");
778                post.add("Refuge");
779                post.add("Sanctuary");
780                post.add("Sanctum");
781                post.add("Shadow");
782                post.add("Shelter");
783                post.add("Safehold");
784                post.add("Terminus");
785                post.add("Principle");
786                post.add("Offering");
787                post.add("Devotion");
788                post.add("Atonement");
789                post.add("Cleansing");
790                post.add("Oblation");
791                post.add("Sacrement");
792                return post.pick();
793        }
794
795        public void commodityUpdated(String commodityId) {
796                CommodityOnMarketAPI com = market.getCommodityData(commodityId);
797                int curr = 0;
798                String modId = market.getId();
799                StatMod mod = com.getAvailableStat().getFlatStatMod(modId);
800                if (mod != null) {
801                        curr = Math.round(mod.value);
802                }
803                
804                int avWithoutPenalties = (int) Math.round(com.getAvailableStat().getBaseValue());
805                for (StatMod m : com.getAvailableStat().getFlatMods().values()) {
806                        if (m.value < 0) continue;
807                        avWithoutPenalties += (int) Math.round(m.value);
808                }
809                
810                int a = com.getAvailable() - curr;
811                a = avWithoutPenalties - curr;
812                int d = com.getMaxDemand();
813                if (d > a) {
814                        //int supply = Math.max(1, d - a - 1);
815                        int supply = Math.max(1, d - a);
816                        com.getAvailableStat().modifyFlat(modId, supply, "Brought in by smugglers");
817                }               
818        }
819
820        public void economyUpdated() {
821                float qualityBonus = 0f;
822                int light = 0;
823                int medium = 0;
824                int heavy = 0;
825                
826                if (large) {
827                        qualityBonus = 0.5f;
828                        light = 4;
829                        medium = 4;
830                        heavy = 3;
831                } else {
832                        qualityBonus = 0f;
833                        light = 3;
834                        medium = 2;
835                        heavy = 1;
836                }
837                
838                market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).
839                                                                        modifyFlatAlways(market.getId(), qualityBonus,
840                                                                        "Development level");
841                
842                float fleetSizeBonus = 0.5f;
843                if (large) fleetSizeBonus = 1f;
844                market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyFlatAlways(market.getId(),
845                                                                        fleetSizeBonus, 
846                                                                        "Development level");
847                
848                String modId = market.getId();
849                market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).modifyFlat(modId, light);
850                market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).modifyFlat(modId, medium);
851                market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).modifyFlat(modId, heavy);
852        }
853
854        public boolean isEconomyListenerExpired() {
855                return isEnded();
856        }
857        
858        public MarketAPI getMarket() {
859                return market;
860        }
861
862
863        protected void setBounty() {
864                
865                List<IntelInfoPlugin> bases = Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class);
866                for (IntelInfoPlugin curr : bases) {
867                        LuddicPathBaseIntel intel = (LuddicPathBaseIntel) curr;
868                        if (intel != this && intel.bountyData != null) {
869                                return;
870                        }
871                }
872                
873                bountyData = new BaseBountyData();
874                float base = 100000f;
875                if (large) {
876                        base = Global.getSettings().getFloat("luddicPathBaseBountyLarge");
877                        bountyData.repChange = 0.05f;
878                } else {
879                        base = Global.getSettings().getFloat("luddicPathBaseBountySmall");
880                        bountyData.repChange = 0.1f;
881                }
882                
883                bountyData.baseBounty = base * (0.9f + (float) Math.random() * 0.2f);
884                bountyData.baseBounty = (int)(bountyData.baseBounty / 10000) * 10000;
885                
886                
887                WeightedRandomPicker<FactionAPI> picker = new WeightedRandomPicker<FactionAPI>();
888                for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, false)) {
889                        FactionAPI faction = cell.getMarket().getFaction();
890                        //if (faction.isPlayerFaction()) continue;
891                        picker.add(faction, (float) Math.pow(2f, cell.getMarket().getSize()));
892                }
893                
894                FactionAPI faction = picker.pick();
895                // player faction is in picker to reduce bounties offered on cells that are already bothering the player
896                if (faction == null || faction.isPlayerFaction()) {
897                        bountyData = null;
898                        return;
899                }
900                
901                bountyData.bountyFaction = faction;
902                bountyData.bountyDuration = 180f;
903                bountyData.bountyElapsedDays = 0f;
904                
905                monthsNoBounty = 0;
906                Misc.makeImportant(entity, "baseBounty");
907                
908                
909//              makeKnown();
910//              sendUpdateIfPlayerHasIntel(bountyData, false);
911                sentBountyUpdate = false;
912        }
913        
914        protected boolean sentBountyUpdate = false;
915        protected void endBounty() {
916                sendUpdateIfPlayerHasIntel(BOUNTY_EXPIRED_PARAM, false);
917                bountyData = null;
918                monthsNoBounty = 0;
919                Misc.makeUnimportant(entity, "baseBounty");
920                sentBountyUpdate = false;
921        }
922        
923        
924        public List<ArrowData> getArrowData(SectorMapAPI map) {
925                return null;
926        }
927
928        public SectorEntityToken getEntity() {
929                return entity;
930        }
931
932        public boolean isLarge() {
933                return large;
934        }
935
936        public PersonAPI getBaseCommander() {
937                return baseCommander;
938        }
939        public void setBaseCommander(PersonAPI baseCommander) {
940                this.baseCommander = baseCommander;
941        }
942}
943
944
945
946
947
948
949
950
951
952
953
954