001package com.fs.starfarer.api.impl.campaign.intel.deciv;
002
003import java.util.ArrayList;
004import java.util.LinkedHashMap;
005import java.util.List;
006import java.util.Random;
007
008import com.fs.starfarer.api.EveryFrameScript;
009import com.fs.starfarer.api.Global;
010import com.fs.starfarer.api.campaign.PlanetAPI;
011import com.fs.starfarer.api.campaign.SectorEntityToken;
012import com.fs.starfarer.api.campaign.econ.Industry;
013import com.fs.starfarer.api.campaign.econ.MarketAPI;
014import com.fs.starfarer.api.campaign.econ.MarketConditionAPI;
015import com.fs.starfarer.api.campaign.econ.SubmarketAPI;
016import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
017import com.fs.starfarer.api.characters.PersonAPI;
018import com.fs.starfarer.api.impl.campaign.DebugFlags;
019import com.fs.starfarer.api.impl.campaign.ids.Conditions;
020import com.fs.starfarer.api.impl.campaign.ids.Factions;
021import com.fs.starfarer.api.impl.campaign.population.CoreImmigrationPluginImpl;
022import com.fs.starfarer.api.util.IntervalUtil;
023import com.fs.starfarer.api.util.Misc;
024import com.fs.starfarer.api.util.TimeoutTracker;
025
026public class DecivTracker implements EveryFrameScript {
027
028        public static final String KEY = "$core_decivTracker";
029        
030        public static final String NO_DECIV_KEY = "$core_noDeciv";
031        
032        public static class MarketDecivData {
033                MarketAPI market;
034                List<Float> stabilityHistory = new ArrayList<Float>();
035        }
036        
037        
038        public static DecivTracker getInstance() {
039                Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY);
040                return (DecivTracker) test; 
041        }
042        
043        public DecivTracker() {
044                super();
045                Global.getSector().getMemoryWithoutUpdate().set(KEY, this);
046        }
047        
048        protected LinkedHashMap<MarketAPI, MarketDecivData> decivData = new LinkedHashMap<MarketAPI, MarketDecivData>();
049        protected IntervalUtil sampler = new IntervalUtil(20f, 40f);
050        protected IntervalUtil checker = new IntervalUtil(5f, 15f);
051        protected TimeoutTracker<String> sentWarning = new TimeoutTracker<String>();
052        protected Random random = new Random();
053        
054        
055        protected Object readResolve() {
056                if (sentWarning == null) {
057                        sentWarning = new TimeoutTracker<String>();
058                }
059                return this;
060        }
061        
062        public void advance(float amount) {
063                
064                float days = Misc.getDays(amount);
065                if (DebugFlags.DECIV_DEBUG) {
066                        days *= 1000f;
067                }
068                
069                sentWarning.advance(days);
070                
071                sampler.advance(days);
072                if (sampler.intervalElapsed()) {
073                        updateSamples();
074                }
075                checker.advance(days);
076                if (checker.intervalElapsed()) {
077                        checkDeciv();
078                }
079        }
080        
081        public MarketDecivData getDataFor(MarketAPI market) {
082                MarketDecivData data = decivData.get(market);
083                if (data == null) {
084                        data = new MarketDecivData();
085                        data.market = market;
086                        decivData.put(market, data);
087                }
088                return data;
089        }
090        
091        public static int getMaxMonths() {
092                return Global.getSettings().getInt("decivSamplingMonths");
093        }
094        public static int getMinStreak() {
095                return Global.getSettings().getInt("decivMinStreak");
096        }
097        public static float getProbPerMonth() {
098                return Global.getSettings().getFloat("decivProbPerMonthOverStreak");
099        }
100        public static float getMinFraction() {
101                return Global.getSettings().getFloat("decivZeroStabilityMinFraction");
102        }
103        
104        
105        protected void updateSamples() {
106                
107                for (MarketAPI market : new ArrayList<MarketAPI>(decivData.keySet())) {
108                        if (!market.isInEconomy()) {
109                                decivData.remove(market);
110                        }
111                }
112                
113                int maxSamples = getMaxMonths();
114                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
115                        MarketDecivData data = getDataFor(market);
116                        
117                        data.stabilityHistory.add(market.getStabilityValue());
118                        while (data.stabilityHistory.size() > maxSamples && !data.stabilityHistory.isEmpty()) {
119                                data.stabilityHistory.remove(0);
120                        }
121                }
122        }
123        
124        protected void checkDeciv() {
125                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
126                        if (checkDeciv(market)) break;
127                }
128        }
129        
130        
131        protected boolean checkDeciv(MarketAPI market) {
132                MarketDecivData data = getDataFor(market);
133                
134//              if (market.getId().contains("chicomoztoc")) {
135//                      decivilize(market, false);
136//                      return true;
137//              }
138                
139                int max = getMaxMonths();
140                int min = getMinStreak();
141                float per = getProbPerMonth();
142                float fraction = getMinFraction();
143                
144                if (data.stabilityHistory.size() < max) return false;
145                if (data.stabilityHistory.get(0) > 0 || market.getStabilityValue() > 0) return false;
146                
147                float streak = 0;
148                float zeroCount = 0;
149                boolean streakEnded = false;
150                for (int i = data.stabilityHistory.size() - 1; i >= 0; i--) {
151                        Float curr = data.stabilityHistory.get(i);
152                        if (curr <= 0) {
153                                zeroCount++;
154                                if (!streakEnded) streak++;
155                        } else {
156                                streakEnded = true;
157                        }
158                }
159                
160                if (streak < min) return false;
161                if (zeroCount / max < fraction) return false;
162                
163                if (Misc.isStoryCritical(market)) return false;
164                
165                float prob = (streak - min) * per;
166                
167                
168                String id = market.getId();
169                if (!sentWarning.contains(id)) {
170                        sendWarning(market);
171                        sentWarning.add(id, 180f);
172                        return false;
173                }
174//              if (prob == 0f) {
175//                      sendWarning(market);
176//                      return false;
177//              }
178                
179                if (random.nextFloat() >= prob) return false;
180                
181                decivilize(market, false);
182                return true;
183        }
184        
185        public static void decivilize(MarketAPI market, boolean fullDestroy) {
186                decivilize(market, fullDestroy, true);
187        }
188        
189        public static void decivilize(MarketAPI market, boolean fullDestroy, boolean withIntel) {
190                if (market.getMemoryWithoutUpdate().getBoolean(NO_DECIV_KEY) && !fullDestroy) return;
191//              System.out.println("Location: " + market.getLocationInHyperspace());
192//              if (true) return;
193                
194                // issues with decivilizing stand-alone stations at the moment since they become treated as planets
195                //if (!(market.getPrimaryEntity() instanceof PlanetAPI)) return;
196                
197                if (market.getPrimaryEntity().isDiscoverable()) return;
198                
199                ListenerUtil.reportColonyAboutToBeDecivilized(market, fullDestroy);
200                
201                if (withIntel) {
202                        DecivIntel intel = new DecivIntel(market, market.getPrimaryEntity(), fullDestroy, false);
203                        Global.getSector().getIntelManager().addIntel(intel);
204                }
205                
206                market.setAdmin(null);
207                
208                for (SectorEntityToken entity : market.getConnectedEntities()) {
209                        entity.setFaction(Factions.NEUTRAL);
210                }
211                
212                market.setPlanetConditionMarketOnly(true);
213                market.setFactionId(Factions.NEUTRAL);
214                
215                market.getCommDirectory().clear();
216                for (PersonAPI person : market.getPeopleCopy()) {
217                        market.removePerson(person);
218                }
219                market.clearCommodities();
220
221                for (MarketConditionAPI mc : new ArrayList<MarketConditionAPI>(market.getConditions())) {
222                        if (mc.getSpec().isDecivRemove()) {
223                                market.removeSpecificCondition(mc.getIdForPluginModifications());
224                        }
225                }
226                
227                for (Industry ind : new ArrayList<Industry>(market.getIndustries())) {
228                        market.removeIndustry(ind.getId(), null, false);
229                }
230                
231                if (!fullDestroy && !market.hasCondition(Conditions.DECIVILIZED)) {
232                        market.addCondition(Conditions.DECIVILIZED);
233                }
234                
235                int size = market.getSize();
236                market.removeCondition(Conditions.RUINS_SCATTERED);
237                market.removeCondition(Conditions.RUINS_WIDESPREAD);
238                market.removeCondition(Conditions.RUINS_EXTENSIVE);
239                market.removeCondition(Conditions.RUINS_VAST);
240                String id = null;
241                if (size <= 3) {
242                        id = market.addCondition(Conditions.RUINS_SCATTERED);
243                } else if (size <= 4) {
244                        id = market.addCondition(Conditions.RUINS_WIDESPREAD);
245                } else if (size <= 6) {
246                        id = market.addCondition(Conditions.RUINS_EXTENSIVE);
247                } else {
248                        id = market.addCondition(Conditions.RUINS_VAST);
249                }
250                if (id != null) {
251                        MarketConditionAPI ruins = market.getSpecificCondition(id);
252                        if (ruins != null) {
253                                ruins.setSurveyed(true);
254                        }
255                }
256                
257                market.getMemoryWithoutUpdate().set("$wasCivilized", true);
258                
259                market.setSize(1);
260                market.getPopulation().setWeight(CoreImmigrationPluginImpl.getWeightForMarketSizeStatic(market.getSize()));
261                market.getPopulation().normalize();
262                
263                for (SubmarketAPI sub : market.getSubmarketsCopy()) {
264                        market.removeSubmarket(sub.getSpecId());
265                }
266                
267                for (SectorEntityToken entity : market.getConnectedEntities()) {
268                        if (!(entity instanceof PlanetAPI)) {
269                                Misc.setAbandonedStationMarket(market.getId() + "_deciv", entity);
270                        }
271                }
272                
273                SectorEntityToken primary = market.getPrimaryEntity();
274                market.getConnectedEntities().clear();
275                market.setPrimaryEntity(primary);
276                market.setPlayerOwned(false);
277                
278                Global.getSector().getEconomy().removeMarket(market);
279                Misc.removeRadioChatter(market);
280                market.advance(0f);
281                
282                ListenerUtil.reportColonyDecivilized(market, fullDestroy);
283                
284//              if (!(market.getPrimaryEntity() instanceof PlanetAPI)) {
285//                      Misc.setAbandonedStationMarket(market.getId() + "_deciv", primary);
286//              }
287
288        }
289        
290        
291        public static void removeColony(MarketAPI market, boolean withRuins) {
292                market.setAdmin(null);
293                
294                for (SectorEntityToken entity : market.getConnectedEntities()) {
295                        entity.setFaction(Factions.NEUTRAL);
296                }
297                
298                market.setPlanetConditionMarketOnly(true);
299                market.setFactionId(Factions.NEUTRAL);
300                
301                market.getCommDirectory().clear();
302                for (PersonAPI person : market.getPeopleCopy()) {
303                        market.removePerson(person);
304                }
305                market.clearCommodities();
306
307                for (MarketConditionAPI mc : new ArrayList<MarketConditionAPI>(market.getConditions())) {
308                        if (mc.getSpec().isDecivRemove()) {
309                                market.removeSpecificCondition(mc.getIdForPluginModifications());
310                        }
311                }
312                
313                for (Industry ind : new ArrayList<Industry>(market.getIndustries())) {
314                        market.removeIndustry(ind.getId(), null, false);
315                }
316                
317                if (withRuins) {
318                        int size = market.getSize();
319                        market.removeCondition(Conditions.RUINS_SCATTERED);
320                        market.removeCondition(Conditions.RUINS_WIDESPREAD);
321                        market.removeCondition(Conditions.RUINS_EXTENSIVE);
322                        market.removeCondition(Conditions.RUINS_VAST);
323                        if (size <= 3) {
324                                market.addCondition(Conditions.RUINS_SCATTERED);
325                        } else if (size <= 4) {
326                                market.addCondition(Conditions.RUINS_WIDESPREAD);
327                        } else if (size <= 6) {
328                                market.addCondition(Conditions.RUINS_EXTENSIVE);
329                        } else {
330                                market.addCondition(Conditions.RUINS_VAST);
331                        }
332                }
333                
334                market.getMemoryWithoutUpdate().set("$wasCivilized", true);
335                
336                market.setSize(1);
337                market.getPopulation().setWeight(CoreImmigrationPluginImpl.getWeightForMarketSizeStatic(market.getSize()));
338                market.getPopulation().normalize();
339                
340                for (SubmarketAPI sub : market.getSubmarketsCopy()) {
341                        market.removeSubmarket(sub.getSpecId());
342                }
343                
344                for (SectorEntityToken entity : market.getConnectedEntities()) {
345                        if (!(entity instanceof PlanetAPI)) {
346                                Misc.setAbandonedStationMarket(market.getId() + "_deciv", entity);
347                        }
348                }
349                
350                market.setIncentiveCredits(0);
351                
352                SectorEntityToken primary = market.getPrimaryEntity();
353                market.getConnectedEntities().clear();
354                market.setPrimaryEntity(primary);
355                market.setPlayerOwned(false);
356                
357                Global.getSector().getEconomy().removeMarket(market);
358                Misc.removeRadioChatter(market);
359                market.advance(0f);
360        }
361        
362        public static void sendWarning(MarketAPI market) {
363                if (market.getMemoryWithoutUpdate().getBoolean(DecivTracker.NO_DECIV_KEY)) return;
364                
365                DecivIntel intel = new DecivIntel(market, market.getPrimaryEntity(), false, true);
366                Global.getSector().getIntelManager().addIntel(intel);
367        }
368
369        public boolean isDone() {
370                return false;
371        }
372
373        public boolean runWhilePaused() {
374                return false;
375        }
376        
377}
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392