001package com.fs.starfarer.api.impl.campaign.intel.events.ht;
002
003import java.util.LinkedHashSet;
004
005import com.fs.starfarer.api.EveryFrameScript;
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.campaign.CampaignFleetAPI;
008import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
009import com.fs.starfarer.api.campaign.PlanetAPI;
010import com.fs.starfarer.api.campaign.SectorEntityToken;
011import com.fs.starfarer.api.campaign.StarSystemAPI;
012import com.fs.starfarer.api.characters.AbilityPlugin;
013import com.fs.starfarer.api.impl.campaign.ids.Abilities;
014import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
015import com.fs.starfarer.api.impl.campaign.ids.Tags;
016import com.fs.starfarer.api.impl.campaign.ids.Terrain;
017import com.fs.starfarer.api.impl.campaign.terrain.PulsarBeamTerrainPlugin;
018import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin;
019import com.fs.starfarer.api.util.IntervalUtil;
020import com.fs.starfarer.api.util.Misc;
021
022/**
023 * @author Alex
024 *
025 * Copyright 2022 Fractal Softworks, LLC
026 */
027public class HTFactorTracker implements EveryFrameScript {
028
029        public static float CHECK_DAYS = 0.05f;
030        
031        protected IntervalUtil interval = new IntervalUtil(CHECK_DAYS * 0.8f, CHECK_DAYS * 1.2f);
032        protected float burnBasedPoints = 0f;
033        protected float daysSinceAtHighBurn = 1f;
034        protected boolean canCheckSB = true;
035        
036        protected LinkedHashSet<String> scanned = new LinkedHashSet<String>();
037        
038        protected Object readResolve() {
039                if (scanned == null) {
040                        scanned = new LinkedHashSet<String>();
041                }
042                return this;
043        }
044        
045        public boolean isDone() {
046                return false;
047        }
048
049        public boolean runWhilePaused() {
050                return false;
051        }
052
053        public void advance(float amount) {
054                float days = Global.getSector().getClock().convertToDays(amount);
055                
056                interval.advance(days);
057                
058                if (interval.intervalElapsed()) {
059                        checkHighBurn(interval.getIntervalDuration());
060                        checkSensorBursts();
061                }
062        }
063
064        protected void checkHighBurn(float days) {
065                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
066                if (pf != null && pf.isInHyperspace()) {
067                        float burn = pf.getCurrBurnLevel();
068                        
069                        if (burn > 20) {
070                                daysSinceAtHighBurn = 0f;
071                        } else {
072                                daysSinceAtHighBurn += days;
073                        }
074
075                        float add = 0f;
076                        float min = 0;
077                        float max = 0;
078                        float f = 0;
079                        if (burn > 40) {
080                                min = HTPoints.PER_DAY_AT_BURN_40;
081                                max = HTPoints.PER_DAY_AT_BURN_50;
082                                f = (Math.min(burn, HTPoints.MAX_BURN_FOR_POINT_GAIN) - 40) / 10f;
083                        } else if (burn > 30) {
084                                min = HTPoints.PER_DAY_AT_BURN_30;
085                                max = HTPoints.PER_DAY_AT_BURN_40;
086                                f = (burn - 30) / 10f;
087                        } else if (burn > 20) {
088                                min = HTPoints.PER_DAY_AT_BURN_20;
089                                max = HTPoints.PER_DAY_AT_BURN_30;
090                                f = (burn - 20) / 10f;
091                        }
092                        
093                        add = min + (max - min) * f;
094                        add *= CHECK_DAYS;
095                        //add *= 100;
096                        
097//                      if (Global.getSettings().isDevMode()) {
098//                              add = 100;
099//                      }
100                        
101                        if (pf.getMemoryWithoutUpdate().getBoolean(MemFlags.NO_HIGH_BURN_TOPOGRAPHY_READINGS)) {
102                                add = 0;
103                        }
104                        
105                        if (add > 0) {
106                                burnBasedPoints += add;
107                                //System.out.println("Added: " + add + ", total: " + burnBasedPoints);
108                        }
109                        int chunk = HTPoints.BURN_POINT_CHUNK_SIZE;
110                        //chunk = 1;
111                        if (burnBasedPoints >= chunk && daysSinceAtHighBurn > 0.3f) {
112                                int mult = (int) burnBasedPoints / chunk;
113                                int points = chunk * mult;
114                                burnBasedPoints -= points;
115                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(new HTHighBurnFactor(points), null);
116                        }
117                } else {
118                        daysSinceAtHighBurn = 1f;
119                }
120        }
121
122        public void checkSensorBursts() {
123                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
124                if (pf == null) return;
125                AbilityPlugin sb = pf.getAbility(Abilities.SENSOR_BURST);
126                if (sb == null) return;
127                
128                if (sb.isUsable() || sb.getLevel() <= 0) {
129                        canCheckSB = true;
130                }
131                
132                if (canCheckSB && !pf.isInHyperspace() && sb.isInProgress() && sb.getLevel() > 0.9f &&
133                                !pf.getContainingLocation().hasTag(Tags.NO_TOPOGRAPHY_SCANS)) {
134                        for (SectorEntityToken entity : pf.getContainingLocation().getAllEntities()) {
135                                checkBlackHole(entity);
136                                checkIonStorm(entity);
137                                checkGasGiant(entity);
138                                checkPulsar(entity);
139                        }
140                        for (CampaignTerrainAPI terrain : pf.getContainingLocation().getTerrainCopy()) {
141                                checkMagneticField(terrain);
142                        }
143                        
144                        checkSystemCenter();
145                        
146                        canCheckSB = false;
147                }
148        }
149        
150        protected void checkBlackHole(SectorEntityToken entity) {
151                if (!(entity instanceof PlanetAPI)) return;
152                
153                PlanetAPI planet = (PlanetAPI) entity;
154                if (!planet.getSpec().isBlackHole()) return;
155                
156                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
157                float dist = Misc.getDistance(pf.getLocation(), entity.getLocation());
158                
159                StarCoronaTerrainPlugin eventHorizon = Misc.getCoronaFor(planet);
160                if (eventHorizon == null) return;
161                
162                String id1 = planet.getId() + "_1";
163                String id2 = planet.getId() + "_2";
164                
165                float closeRange = planet.getRadius() + 300f;
166                
167                if (dist < closeRange) {
168                        if (scanned.contains(id2)) {
169                                addMessage("Black hole already scanned at short range");
170                        } else {
171                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
172                                                new HTScanFactor("Black hole scanned at short range (" + planet.getName() + ")", HTPoints.SCAN_BLACK_HOLE_SHORT_RANGE), null);
173                                scanned.add(id2);
174                        }
175                } else if (eventHorizon.containsEntity(pf)) {
176                        if (scanned.contains(id1)) {
177                                addMessage("Black hole already scanned at long range");
178                        } else {
179                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
180                                                new HTScanFactor("Black hole scanned at long range (" + planet.getName() + ")", HTPoints.SCAN_BLACK_HOLE_LONG_RANGE), null);
181                                scanned.add(id1);
182                        }
183                }
184        }
185        
186        protected void checkIonStorm(SectorEntityToken entity) {
187                if (!(entity instanceof PlanetAPI)) return;
188                
189                PlanetAPI planet = (PlanetAPI) entity;
190                if (!planet.isGasGiant()) return;
191                
192                StarCoronaTerrainPlugin ionStorm = Misc.getCoronaFor(planet);
193                if (ionStorm == null) return;
194                
195                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
196                
197                String id = ionStorm.getEntity().getId();
198                
199                if (ionStorm.containsEntity(pf)) {
200                        if (scanned.contains(id)) {
201                                addMessage("Ion storm already scanned");
202                        } else {
203                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
204                                                new HTScanFactor("Ion storm scanned (" + planet.getName() + ")", HTPoints.SCAN_ION_STORM), null);
205                                scanned.add(id);
206                        }
207                }
208        }
209        
210        protected void checkMagneticField(CampaignTerrainAPI terrain) {
211                if (terrain.getPlugin() == null) return;
212                if (!Terrain.MAGNETIC_FIELD.equals(terrain.getType())) return;
213
214                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
215                
216                String id = terrain.getId();
217                
218                if (terrain.getPlugin().containsEntity(pf)) {
219                        if (scanned.contains(id)) {
220                                addMessage("Magnetic field already scanned");
221                        } else {
222                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
223                                                new HTScanFactor("Magnetic field scanned", HTPoints.SCAN_MAGNETIC_FIELD), null);
224                                scanned.add(id);
225                        }
226                }
227        }
228        
229        protected void checkGasGiant(SectorEntityToken entity) {
230                if (!(entity instanceof PlanetAPI)) return;
231                
232                PlanetAPI planet = (PlanetAPI) entity;
233                if (!planet.isGasGiant()) return;
234                
235                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
236                String id = planet.getId();
237                
238                float dist = Misc.getDistance(pf.getLocation(), entity.getLocation());
239                boolean inRange = dist < 500f + planet.getRadius();
240                if (inRange) {
241                        if (scanned.contains(id)) {
242                                addMessage("Gas giant already scanned");
243                        } else {
244                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
245                                                new HTScanFactor("Gas giant scanned (" + planet.getName() + ")", HTPoints.SCAN_GAS_GIANT), null);
246                                scanned.add(id);
247                        }
248                }
249        }
250        
251        protected void checkSystemCenter() {
252                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
253                StarSystemAPI system = pf.getStarSystem();
254                if (system == null) return;
255                
256                String type = null;
257                int points = 0;
258                switch (system.getType()) {
259                case BINARY_CLOSE:
260                        type = "Center of binary system";
261                        points = HTPoints.SCAN_BINARY;
262                        break;
263                case NEBULA:
264                        type = "Center of starless nebula";
265                        points = HTPoints.SCAN_NEBULA;
266                        break;
267                case TRINARY_1CLOSE_1FAR:
268                        type = "Center of binary system";
269                        points = HTPoints.SCAN_BINARY;
270                        break;
271                case TRINARY_2CLOSE:
272                        type = "Center of trinary system";
273                        points = HTPoints.SCAN_TRINARY;
274                        break;
275                default:
276                        int count = 0;
277                        for (PlanetAPI curr : system.getPlanets()) {
278                                if (!curr.isStar()) continue;
279                                float dist = Misc.getDistance(curr.getLocation(), pf.getLocation());
280                                if (dist < 2000 + curr.getRadius()) {
281                                        count++;
282                                }
283                        }
284                        if (count > 1) {
285                                type = "Stellar conflux";
286                                if (count == 2) {
287                                        points = HTPoints.SCAN_BINARY;
288                                } else {
289                                        points = HTPoints.SCAN_TRINARY;
290                                }
291                        }
292                        break;
293                }
294                if (type == null) return;
295                
296                String id = "systemtypescan_" + system.getId();
297                float range = pf.getLocation().length();
298                
299                if (range < 2000) {
300                        if (scanned.contains(id)) {
301                                addMessage("Center of star system already scanned");
302                        } else {
303                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
304                                                new HTScanFactor(type + " scanned (" + system.getBaseName() + ")", points), null);
305                                scanned.add(id);
306                        }
307                }
308        }
309        
310        protected void checkPulsar(SectorEntityToken entity) {
311                if (!(entity instanceof PlanetAPI)) return;
312                
313                PlanetAPI planet = (PlanetAPI) entity;
314                if (!planet.getSpec().isPulsar()) return;
315                
316                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
317                
318                StarCoronaTerrainPlugin corona = Misc.getCoronaFor(planet);
319                if (corona == null) return;
320                
321                PulsarBeamTerrainPlugin pulsar = Misc.getPulsarFor(planet);
322                if (pulsar == null) return;
323                
324                String id1 = planet.getId() + "_1";
325                String id2 = planet.getId() + "_2";
326                
327                if (corona.containsEntity(pf)) {
328                        if (scanned.contains(id2)) {
329                                addMessage("Neutron star already scanned");
330                        } else {
331                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
332                                                new HTScanFactor("Neutron star scanned (" + planet.getName() + ")", HTPoints.SCAN_NEUTRON_STAR), null);
333                                scanned.add(id2);
334                        }
335                }
336                
337                if (pulsar.containsEntity(pf)) {
338                        if (scanned.contains(id1)) {
339                                addMessage("Pulsar beam already scanned");
340                        } else {
341                                HyperspaceTopographyEventIntel.addFactorCreateIfNecessary(
342                                                new HTScanFactor("Pulsar beam scanned (" + planet.getName() + ")", HTPoints.SCAN_PULSAR_BEAM), null);
343                                scanned.add(id1);
344                        }
345                }
346        }
347
348        
349        protected void addMessage(String text) {
350                Global.getSector().getCampaignUI().getMessageDisplay().addMessage(text + ", no new topographic data acquired", Misc.getNegativeHighlightColor());
351        }
352}
353
354
355
356