001package com.fs.starfarer.api.impl.campaign.intel.bases;
002
003import java.util.ArrayList;
004import java.util.Iterator;
005import java.util.LinkedHashSet;
006import java.util.List;
007import java.util.Random;
008import java.util.Set;
009
010import com.fs.starfarer.api.EveryFrameScript;
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.campaign.FactionAPI;
013import com.fs.starfarer.api.campaign.StarSystemAPI;
014import com.fs.starfarer.api.campaign.econ.MarketAPI;
015import com.fs.starfarer.api.impl.campaign.DebugFlags;
016import com.fs.starfarer.api.impl.campaign.ids.Conditions;
017import com.fs.starfarer.api.impl.campaign.ids.Factions;
018import com.fs.starfarer.api.impl.campaign.ids.Tags;
019import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel.PirateBaseTier;
020import com.fs.starfarer.api.impl.campaign.intel.events.PiracyRespiteScript;
021import com.fs.starfarer.api.util.IntervalUtil;
022import com.fs.starfarer.api.util.Misc;
023import com.fs.starfarer.api.util.WeightedRandomPicker;
024
025public class PlayerRelatedPirateBaseManager implements EveryFrameScript {
026
027        public static final String KEY = "$core_PR_pirateBaseManager";
028        
029        
030        //public static int MIN_MONTHS_BEFORE_RAID = Global.getSettings().getInt("minMonthsBeforeFirstPirateRaidOnPlayerColony");
031        
032        public static int MIN_TIMEOUT = Global.getSettings().getIntFromArray("playerRelatedPirateBaseCreationTimeoutMonths", 0); 
033        public static int MAX_TIMEOUT = Global.getSettings().getIntFromArray("playerRelatedPirateBaseCreationTimeoutMonths", 1);
034        
035        public static int MIN_TIMEOUT_DESTROYED = Global.getSettings().getIntFromArray("playerRelatedPirateBaseCreationTimeoutExtraAfterBaseDestroyed", 0); 
036        public static int MAX_TIMEOUT_DESTROYED  = Global.getSettings().getIntFromArray("playerRelatedPirateBaseCreationTimeoutExtraAfterBaseDestroyed", 1); 
037        
038        
039        public static PlayerRelatedPirateBaseManager getInstance() {
040                Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY);
041                return (PlayerRelatedPirateBaseManager) test; 
042        }
043        
044        
045        protected long start = 0;
046        //protected boolean sentFirstRaid = false;
047        protected IntervalUtil monthlyInterval = new IntervalUtil(20f, 40f);
048        protected int monthsPlayerColoniesExist = 0;
049        protected int baseCreationTimeout = 0;
050        protected Random random = new Random();
051        
052        protected List<PirateBaseIntel> bases = new ArrayList<PirateBaseIntel>();
053        
054        public PlayerRelatedPirateBaseManager() {
055                super();
056                Global.getSector().getMemoryWithoutUpdate().set(KEY, this);
057                start = Global.getSector().getClock().getTimestamp();
058        }
059        
060        
061        public void advance(float amount) {
062                
063                for (PirateBaseIntel intel : bases) {
064                        intel.advance(amount);
065                }
066                
067                float days = Misc.getDays(amount);
068                
069                if (DebugFlags.RAID_DEBUG) {
070                        days *= 100f;
071                }
072                
073                monthlyInterval.advance(days);
074                
075                if (monthlyInterval.intervalElapsed()) {
076                        removeDestroyedBases();
077                        
078                        FactionAPI player = Global.getSector().getPlayerFaction();
079                        List<MarketAPI> markets = Misc.getFactionMarkets(player);
080                        
081                        Iterator<MarketAPI> iter = markets.iterator();
082                        while (iter.hasNext()) {
083                                if (iter.next().isHidden()) iter.remove();
084                        }
085                        
086                        if (markets.isEmpty()) {
087                                return;
088                        }
089                        
090                        monthsPlayerColoniesExist++;
091                        
092//                      if (!sentFirstRaid) {
093//                              if (monthsPlayerColoniesExist >= MIN_MONTHS_BEFORE_RAID && !markets.isEmpty()) {
094//                                      sendFirstRaid(markets);
095//                                      baseCreationTimeout = MIN_TIMEOUT + random.nextInt(MAX_TIMEOUT - MIN_TIMEOUT + 1);
096//                              }
097//                              return;
098//                      }
099                        
100                        if (baseCreationTimeout > 0) {
101                                baseCreationTimeout--;
102                        } else {
103                                if (random.nextFloat() > 0.5f && PiracyRespiteScript.get() == null) {
104                                        addBasesAsNeeded();
105                                }
106                        }
107                }
108        }
109        
110        protected void removeDestroyedBases() {
111                Iterator<PirateBaseIntel> iter = bases.iterator();
112                while (iter.hasNext()) {
113                        PirateBaseIntel intel = iter.next();
114                        if (intel.isEnded() && !intel.getMarket().isInEconomy()) {
115                                iter.remove();
116                                
117//                              int baseTimeout = 3;
118//                              switch (intel.getTier()) {
119//                              case TIER_1_1MODULE: baseTimeout = 3; break;
120//                              case TIER_2_1MODULE: baseTimeout = 3; break;
121//                              case TIER_3_2MODULE: baseTimeout = 4; break;
122//                              case TIER_4_3MODULE: baseTimeout = 5; break;
123//                              case TIER_5_3MODULE: baseTimeout = 6; break;
124//                              }
125//                              baseCreationTimeout += baseTimeout + random.nextInt(baseTimeout + 1);
126                                baseCreationTimeout += MIN_TIMEOUT_DESTROYED + random.nextInt(MAX_TIMEOUT_DESTROYED - MIN_TIMEOUT_DESTROYED + 1);
127                        }
128                }
129        }
130        
131        protected void addBasesAsNeeded() {
132                FactionAPI player = Global.getSector().getPlayerFaction();
133                List<MarketAPI> markets = Misc.getFactionMarkets(player);
134                
135                Set<StarSystemAPI> systems = new LinkedHashSet<StarSystemAPI>();
136                for (MarketAPI curr : markets) {
137                        StarSystemAPI system = curr.getStarSystem();
138                        if (system != null) {
139                                systems.add(system);
140                        }
141                }
142                if (systems.isEmpty()) return;
143                
144                float marketTotal = markets.size();
145                int numBases = (int) (marketTotal / 2);
146                if (numBases < 1) numBases = 1;
147                if (numBases > 2) numBases = 2;
148                
149                
150                if (bases.size() >= numBases) {
151                        return;
152                }
153                
154                
155                StarSystemAPI initialTarget = null;
156                float bestWeight = 0f;
157                OUTER: for (StarSystemAPI curr : systems) {
158                        float w = 0f;
159                        for (MarketAPI m : Global.getSector().getEconomy().getMarkets(curr)) {
160                                if (m.hasCondition(Conditions.PIRATE_ACTIVITY)) continue OUTER;
161                                if (m.getFaction().isPlayerFaction()) {
162                                        w += m.getSize() * m.getSize();
163                                }
164                        }
165                        if (w > bestWeight) {
166                                bestWeight = w;
167                                initialTarget = curr;
168                        }
169                }
170                
171                if (initialTarget == null) return;
172                
173                StarSystemAPI target = pickSystemForPirateBase(initialTarget);
174                if (target == null) return;
175                
176                PirateBaseTier tier = pickTier(target);
177                
178                String factionId = pickPirateFaction();
179                if (factionId == null) return;
180                
181                //factionId = Factions.HEGEMONY;
182                
183                PirateBaseIntel intel = new PirateBaseIntel(target, factionId, tier);
184                if (intel.isDone()) {
185                        intel = null;
186                        return;
187                }
188                
189                //intel.setTargetPlayerColoniesOnly(true);
190                // this is for raids: don't do it since raids are handled by HostileActivityEventIntel now
191                //intel.setForceTarget(initialTarget);
192                intel.updateTarget();
193                bases.add(intel);
194                
195                baseCreationTimeout = MIN_TIMEOUT + random.nextInt(MAX_TIMEOUT - MIN_TIMEOUT + 1);
196        }
197
198        public String pickPirateFaction() {
199                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
200                for (FactionAPI faction : Global.getSector().getAllFactions()) {
201                        if (!faction.isHostileTo(Factions.PLAYER)) continue;
202                        
203                        if (faction.getCustomBoolean(Factions.CUSTOM_MAKES_PIRATE_BASES)) {
204                                picker.add(faction.getId(), 1f);
205                        }
206                }
207                return picker.pick();
208        }
209        
210//      protected void sendFirstRaid(List<MarketAPI> markets) {
211//              if (markets.isEmpty()) return;
212//              
213//              
214//              WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>(random);
215//              picker.addAll(markets);
216//              MarketAPI target = picker.pick();
217//              
218//              PirateBaseIntel closest = null;
219//              float minDist = Float.MAX_VALUE;
220//              for (IntelInfoPlugin p : Global.getSector().getIntelManager().getIntel(PirateBaseIntel.class)) {
221//                      PirateBaseIntel intel = (PirateBaseIntel) p;
222//                      if (intel.isEnding()) continue;
223//                      
224//                      float dist = Misc.getDistanceLY(intel.getMarket().getPrimaryEntity(), target.getPrimaryEntity());
225//                      if (dist < minDist && dist <= 15) {
226//                              minDist = dist;
227//                              closest = intel;
228//                      }
229//              }
230//              
231//              if (closest != null && target != null) {
232//                      float raidFP = 120 + 30f * random.nextFloat();
233////                    raidFP = 1000;
234////                    raidFP = 500;
235//                      closest.startRaid(target.getStarSystem(), raidFP);
236//                      sentFirstRaid = true;
237//              }
238//      }
239
240        
241        
242        protected PirateBaseTier pickTier(StarSystemAPI system) {
243                float max = 0f;
244                for (MarketAPI m : Global.getSector().getEconomy().getMarkets(system)) {
245                        if (m.getFaction().isPlayerFaction()) {
246                                max = Math.max(m.getSize(), max);
247                        }
248                }
249                if (max >= 7) {
250                        return PirateBaseTier.TIER_5_3MODULE;
251                } else if (max >= 6) {
252                        return PirateBaseTier.TIER_4_3MODULE;
253                } else if (max >= 5) {
254                        return PirateBaseTier.TIER_3_2MODULE;
255                } else if (max >= 4) {
256                        return PirateBaseTier.TIER_2_1MODULE;
257                } else {
258                        return PirateBaseTier.TIER_1_1MODULE;
259                }
260                
261        }
262        
263        protected StarSystemAPI pickSystemForPirateBase(StarSystemAPI initialTarget) {
264                WeightedRandomPicker<StarSystemAPI> veryFar = new WeightedRandomPicker<StarSystemAPI>(random);
265                WeightedRandomPicker<StarSystemAPI> far = new WeightedRandomPicker<StarSystemAPI>(random);
266                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(random);
267                
268                for (StarSystemAPI system : Global.getSector().getStarSystems()) {
269                        if (system.hasPulsar()) continue;
270                        
271                        float days = Global.getSector().getClock().getElapsedDaysSince(system.getLastPlayerVisitTimestamp());
272                        if (days < 180f) continue;
273                        
274                        if (system.getCenter().getMemoryWithoutUpdate().contains(PirateBaseManager.RECENTLY_USED_FOR_BASE)) continue;
275                        
276                        float weight = 0f;
277                        if (system.hasTag(Tags.THEME_MISC_SKIP)) {
278                                weight = 1f;
279                        } else if (system.hasTag(Tags.THEME_MISC)) {
280                                weight = 3f;
281                        } else if (system.hasTag(Tags.THEME_REMNANT_NO_FLEETS)) {
282                                weight = 3f;
283                        } else if (system.hasTag(Tags.THEME_REMNANT_DESTROYED)) {
284                                weight = 3f;
285                        } else if (system.hasTag(Tags.THEME_RUINS)) {
286                                weight = 5f;
287                        } else if (system.hasTag(Tags.THEME_CORE_UNPOPULATED)) {
288                                //weight = 1f;
289                                weight = 0f;
290                        }
291                        if (weight <= 0f) continue;
292                        
293                        float usefulStuff = system.getCustomEntitiesWithTag(Tags.OBJECTIVE).size() +
294                                                                system.getCustomEntitiesWithTag(Tags.STABLE_LOCATION).size();
295                        if (usefulStuff <= 0) continue;
296                        
297                        if (Misc.getMarketsInLocation(system).size() > 0) continue;
298                        
299                        float dist = Misc.getDistance(initialTarget.getLocation(), system.getLocation());
300                        
301                        float distMult = 100000f / dist;
302                        distMult *= distMult;
303                        
304                        if (dist > 30000f) {
305                                veryFar.add(system, weight * usefulStuff * distMult);
306                        } else if (dist > 10000f) {
307                                far.add(system, weight * usefulStuff * distMult);
308                        } else {
309                                picker.add(system, weight * usefulStuff * distMult);
310                        }
311                }
312                
313                if (picker.isEmpty()) {
314                        picker.addAll(far);
315                }
316                if (picker.isEmpty()) {
317                        picker.addAll(veryFar);
318                }
319                
320                return picker.pick();
321        }
322
323
324        public boolean isDone() {
325                return false;
326        }
327
328
329        public boolean runWhilePaused() {
330                return false;
331        }
332        
333}
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348