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