001package com.fs.starfarer.api.impl.campaign.intel.bases;
002
003import java.util.Random;
004
005import com.fs.starfarer.api.EveryFrameScript;
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.campaign.FactionAPI;
008import com.fs.starfarer.api.campaign.StarSystemAPI;
009import com.fs.starfarer.api.impl.campaign.Tuning;
010import com.fs.starfarer.api.impl.campaign.ids.Factions;
011import com.fs.starfarer.api.impl.campaign.ids.Tags;
012import com.fs.starfarer.api.impl.campaign.intel.BaseEventManager;
013import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel.PirateBaseTier;
014import com.fs.starfarer.api.util.Misc;
015import com.fs.starfarer.api.util.WeightedRandomPicker;
016
017public class PirateBaseManager extends BaseEventManager {
018
019        public static final String KEY = "$core_pirateBaseManager";
020        
021        public static final float CHECK_DAYS = 10f;
022        public static final float CHECK_PROB = 0.5f;
023        
024        
025        public static PirateBaseManager getInstance() {
026                Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY);
027                return (PirateBaseManager) test; 
028        }
029        
030        protected long start = 0;
031        protected float extraDays = 0;
032        
033        protected int numDestroyed = 0;
034        protected int numSpawnChecksToSkip = 0;
035        
036        public PirateBaseManager() {
037                super();
038                Global.getSector().getMemoryWithoutUpdate().set(KEY, this);
039                start = Global.getSector().getClock().getTimestamp();
040        }
041        
042        @Override
043        protected int getMinConcurrent() {
044                return Global.getSettings().getInt("minPirateBases");
045        }
046        @Override
047        protected int getMaxConcurrent() {
048                return Global.getSettings().getInt("maxPirateBases");
049        }
050        
051        @Override
052        protected float getBaseInterval() {
053                return CHECK_DAYS;
054        }
055        
056        
057        @Override
058        public void advance(float amount) {
059                super.advance(amount);
060        }
061
062
063
064
065
066        protected Random random = new Random();
067        @Override
068        protected EveryFrameScript createEvent() {
069                if (numSpawnChecksToSkip > 0) {
070                        numSpawnChecksToSkip--;
071                        return null;
072                }
073                
074                if (random.nextFloat() < CHECK_PROB) return null;
075                
076                StarSystemAPI system = pickSystemForPirateBase();
077                if (system == null) return null;
078                
079                //PirateBaseIntel intel = new PirateBaseIntel(system, Factions.PIRATES, PirateBaseTier.TIER_5_3MODULE);
080                //PirateBaseIntel intel = new PirateBaseIntel(system, Factions.PIRATES, PirateBaseTier.TIER_3_2MODULE);
081                PirateBaseTier tier = pickTier();
082                
083                //tier = PirateBaseTier.TIER_5_3MODULE;
084                
085                String factionId = pickPirateFaction();
086                if (factionId == null) return null;
087                
088                PirateBaseIntel intel = new PirateBaseIntel(system, factionId, tier);
089                if (intel.isDone()) intel = null;
090
091                return intel;
092        }
093        
094        public String pickPirateFaction() {
095                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
096                for (FactionAPI faction : Global.getSector().getAllFactions()) {
097                        if (faction.getCustomBoolean(Factions.CUSTOM_MAKES_PIRATE_BASES)) {
098                                picker.add(faction.getId(), 1f);
099                        }
100                }
101                return picker.pick();
102        }
103        
104        public float getUnadjustedDaysSinceStart() {
105                float days = Global.getSector().getClock().getElapsedDaysSince(start);
106                return days;
107        }
108        
109        public float getDaysSinceStart() {
110                float days = Global.getSector().getClock().getElapsedDaysSince(start) + extraDays;
111                if (Misc.isFastStartExplorer()) {
112                        days += Tuning.FAST_START_EXTRA_DAYS - 30f;
113                } else if (Misc.isFastStart()) {
114                        days += Tuning.FAST_START_EXTRA_DAYS + 60f;
115                }
116                return days;
117        }
118        
119        /**
120         * 0 at six months (depending on start option chosen), goes up to 1 two years later.
121         * @return
122         */
123        public float getStandardTimeFactor() {
124                float timeFactor = (PirateBaseManager.getInstance().getDaysSinceStart() - Tuning.FAST_START_EXTRA_DAYS) / (Tuning.DAYS_UNTIL_FULL_TIME_FACTOR);
125                if (timeFactor < 0) timeFactor = 0;
126                if (timeFactor > 1) timeFactor = 1;
127                return timeFactor;
128        }
129        
130        public float getExtraDays() {
131                return extraDays;
132        }
133
134        public void setExtraDays(float extraDays) {
135                this.extraDays = extraDays;
136        }
137
138        protected PirateBaseTier pickTier() {
139                float days = getDaysSinceStart();
140                
141                days += numDestroyed * 200;
142                
143                WeightedRandomPicker<PirateBaseTier> picker = new WeightedRandomPicker<PirateBaseTier>();
144                
145                if (days < 360) {
146                        picker.add(PirateBaseTier.TIER_1_1MODULE, 10f);
147                        picker.add(PirateBaseTier.TIER_2_1MODULE, 10f);
148                } else if (days < 720f) {
149                        picker.add(PirateBaseTier.TIER_2_1MODULE, 10f);
150                        picker.add(PirateBaseTier.TIER_3_2MODULE, 10f);
151                } else if (days < 1080f) {
152                        picker.add(PirateBaseTier.TIER_3_2MODULE, 10f);
153                        picker.add(PirateBaseTier.TIER_4_3MODULE, 10f);
154                } else {
155                        picker.add(PirateBaseTier.TIER_3_2MODULE, 10f);
156                        picker.add(PirateBaseTier.TIER_4_3MODULE, 10f);
157                        picker.add(PirateBaseTier.TIER_5_3MODULE, 10f);
158                }
159                
160                
161//              if (true) {
162//                      picker.clear();
163//                      picker.add(PirateBaseTier.TIER_1_1MODULE, 10f);
164//                      picker.add(PirateBaseTier.TIER_2_1MODULE, 10f);
165//                      picker.add(PirateBaseTier.TIER_3_2MODULE, 10f);
166//                      picker.add(PirateBaseTier.TIER_4_3MODULE, 10f);
167//                      picker.add(PirateBaseTier.TIER_5_3MODULE, 10f);
168//              }
169                
170                
171                return picker.pick();
172        }
173        
174        public static String RECENTLY_USED_FOR_BASE = "$core_recentlyUsedForBase";
175        public static float genBaseUseTimeout() {
176                return 120f + 60f * (float) Math.random();
177        }
178        public static void markRecentlyUsedForBase(StarSystemAPI system) {
179                if (system != null && system.getCenter() != null) {
180                        system.getCenter().getMemoryWithoutUpdate().set(RECENTLY_USED_FOR_BASE, true, genBaseUseTimeout());
181                }
182        }
183        
184        protected StarSystemAPI pickSystemForPirateBase() {
185                WeightedRandomPicker<StarSystemAPI> far = new WeightedRandomPicker<StarSystemAPI>(random);
186                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(random);
187                
188                for (StarSystemAPI system : Global.getSector().getStarSystems()) {
189                        if (system.hasPulsar()) continue;
190                        if (system.hasTag(Tags.THEME_SPECIAL)) continue;
191                        if (system.hasTag(Tags.THEME_HIDDEN)) continue;
192                        
193                        float days = Global.getSector().getClock().getElapsedDaysSince(system.getLastPlayerVisitTimestamp());
194                        if (days < 45f) continue;
195                        if (system.getCenter().getMemoryWithoutUpdate().contains(RECENTLY_USED_FOR_BASE)) continue;
196                        
197                        float weight = 0f;
198                        if (system.hasTag(Tags.THEME_MISC_SKIP)) {
199                                weight = 1f;
200                        } else if (system.hasTag(Tags.THEME_MISC)) {
201                                weight = 3f;
202                        } else if (system.hasTag(Tags.THEME_REMNANT_NO_FLEETS)) {
203                                weight = 3f;
204                        } else if (system.hasTag(Tags.THEME_REMNANT_DESTROYED)) {
205                                weight = 3f;
206                        } else if (system.hasTag(Tags.THEME_RUINS)) {
207                                weight = 5f;
208                        } else if (system.hasTag(Tags.THEME_CORE_UNPOPULATED)) {
209                                //weight = 1f;
210                                weight = 0f;
211                        }
212                        if (weight <= 0f) continue;
213                        
214                        float usefulStuff = system.getCustomEntitiesWithTag(Tags.OBJECTIVE).size() +
215                                                                system.getCustomEntitiesWithTag(Tags.STABLE_LOCATION).size();
216                        if (usefulStuff <= 0) continue;
217                        
218                        if (Misc.hasPulsar(system)) continue;
219                        if (Misc.getMarketsInLocation(system).size() > 0) continue;
220                        
221                        float dist = system.getLocation().length();
222                        
223                        
224                        
225//                      float distMult = 1f - dist / 20000f;
226//                      if (distMult > 1f) distMult = 1f;
227//                      if (distMult < 0.1f) distMult = 0.1f;
228                        
229                        float distMult = 1f;
230                        
231                        if (dist > 36000f) {
232                                far.add(system, weight * usefulStuff * distMult);
233                        } else {
234                                picker.add(system, weight * usefulStuff * distMult);
235                        }
236                }
237                
238                if (picker.isEmpty()) {
239                        picker.addAll(far);
240                }
241                
242                return picker.pick();
243        }
244
245        public int getNumDestroyed() {
246                return numDestroyed;
247        }
248
249        public void setNumDestroyed(int numDestroyed) {
250                this.numDestroyed = numDestroyed;
251        }
252        
253        public void incrDestroyed() {
254                numDestroyed++;
255                numSpawnChecksToSkip = Math.max(numSpawnChecksToSkip, (Tuning.PIRATE_BASE_MIN_TIMEOUT_MONTHS + 
256                                        Misc.random.nextInt(Tuning.PIRATE_BASE_MAX_TIMEOUT_MONTHS - Tuning.PIRATE_BASE_MIN_TIMEOUT_MONTHS + 1))
257                                * 3); // checks happen every 10 days on average, *3 to get months
258        }
259        
260}
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275