Starsector API
Loading...
Searching...
No Matches
SensorGhostManager.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.ghosts;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5import java.util.List;
6import java.util.Random;
7
8import com.fs.starfarer.api.EveryFrameScript;
9import com.fs.starfarer.api.Global;
10import com.fs.starfarer.api.campaign.CampaignFleetAPI;
11import com.fs.starfarer.api.campaign.SectorEntityToken;
12import com.fs.starfarer.api.impl.campaign.ghosts.types.AbyssalDrifterGhostCreator;
13import com.fs.starfarer.api.impl.campaign.ghosts.types.ChargerGhostCreator;
14import com.fs.starfarer.api.impl.campaign.ghosts.types.EchoGhostCreator;
15import com.fs.starfarer.api.impl.campaign.ghosts.types.EncounterTricksterGhostCreator;
16import com.fs.starfarer.api.impl.campaign.ghosts.types.GuideGhostCreator;
17import com.fs.starfarer.api.impl.campaign.ghosts.types.LeviathanCalfGhostCreator;
18import com.fs.starfarer.api.impl.campaign.ghosts.types.LeviathanGhostCreator;
19import com.fs.starfarer.api.impl.campaign.ghosts.types.MinnowGhostCreator;
20import com.fs.starfarer.api.impl.campaign.ghosts.types.NoGhostCreator;
21import com.fs.starfarer.api.impl.campaign.ghosts.types.RacerGhostCreator;
22import com.fs.starfarer.api.impl.campaign.ghosts.types.RemnantGhostCreator;
23import com.fs.starfarer.api.impl.campaign.ghosts.types.RemoraGhostCreator;
24import com.fs.starfarer.api.impl.campaign.ghosts.types.ShipGhostCreator;
25import com.fs.starfarer.api.impl.campaign.ghosts.types.StormTricksterGhostCreator;
26import com.fs.starfarer.api.impl.campaign.ghosts.types.StormcallerGhostCreator;
27import com.fs.starfarer.api.impl.campaign.ghosts.types.ZigguratGhostCreator;
28import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
29import com.fs.starfarer.api.util.Misc;
30import com.fs.starfarer.api.util.TimeoutTracker;
31import com.fs.starfarer.api.util.WeightedRandomPicker;
32
33public class SensorGhostManager implements EveryFrameScript {
34
35 public static List<SensorGhostCreator> CREATORS = new ArrayList<SensorGhostCreator>();
36 static {
37 //CREATORS.add(new TestGhostCreator());
38 CREATORS.add(new ChargerGhostCreator());
39 CREATORS.add(new EchoGhostCreator());
40 CREATORS.add(new EncounterTricksterGhostCreator());
41 CREATORS.add(new GuideGhostCreator());
42 CREATORS.add(new LeviathanGhostCreator());
43 CREATORS.add(new LeviathanCalfGhostCreator());
44 CREATORS.add(new MinnowGhostCreator());
45 CREATORS.add(new NoGhostCreator());
46 CREATORS.add(new RacerGhostCreator());
47 CREATORS.add(new RemnantGhostCreator());
48 CREATORS.add(new RemoraGhostCreator());
49 CREATORS.add(new ShipGhostCreator());
50 CREATORS.add(new StormcallerGhostCreator());
51 CREATORS.add(new StormTricksterGhostCreator());
52 CREATORS.add(new ZigguratGhostCreator());
53 CREATORS.add(new AbyssalDrifterGhostCreator());
54 }
55
56
57 public static float GHOST_SPAWN_RATE_MULT = 0.75f;
58
59 public static float GHOST_SPAWN_RATE_MULT_IN_ABYSS = 3f;
60
61 public static float SB_ATTRACT_GHOSTS_PROBABILITY = 0.5f;
62 public static float SB_FAILED_TO_ATTRACT_TIMEOUT_MULT = 0.25f;
63 public static float MIN_SB_TIMEOUT = 5f;
64 public static float MAX_SB_TIMEOUT = 20f;
65 public static float MIN_FULL_GHOST_TIMEOUT_DAYS = 10f;
66 public static float MAX_FULL_GHOST_TIMEOUT_DAYS = 40f;
67 public static float MIN_SHORT_GHOST_TIMEOUT_DAYS = 0f;
68 public static float MAX_SHORT_GHOST_TIMEOUT_DAYS = 0.2f;
69 public static float FULL_TIMEOUT_TRIGGER_PROBABILITY = 0.95f; // chance spawning a ghost triggers the full timeout
70
71
72 public static float MIN_FAILED_CREATOR_TIMEOUT_DAYS = 0.8f;
73 public static float MAX_FAILED_CREATOR_TIMEOUT_DAYS = 1.2f;
74
75
76 protected TimeoutTracker<String> perCreatorTimeouts = new TimeoutTracker<String>();
77 protected float timeoutRemaining = 0f;
78 protected float sbTimeoutRemaining = 0f;
79 protected Random random = new Random(Misc.genRandomSeed());
80 protected List<SensorGhost> ghosts = new ArrayList<SensorGhost>();
81 protected boolean spawnTriggeredBySensorBurst = false;
82
84 String ghostManagerKey = "$ghostManager";
85 SensorGhostManager manager = (SensorGhostManager) Global.getSector().getMemoryWithoutUpdate().get(ghostManagerKey);
86 if (manager == null) {
87 for (EveryFrameScript curr : Global.getSector().getScripts()) {
88 if (curr instanceof SensorGhostManager) {
89 manager = (SensorGhostManager) curr;
90 Global.getSector().getMemoryWithoutUpdate().set(ghostManagerKey, manager);
91 break;
92 }
93 }
94 }
95 return manager;
96 }
97
98 public static SensorGhost getGhostFor(SectorEntityToken entity) {
100 if (manager == null) return null;
101
102 for (SensorGhost ghost : manager.ghosts) {
103 if (ghost.getEntity() == entity) {
104 return ghost;
105 }
106 }
107 return null;
108 }
109
110 public void advance(float amount) {
111 if (amount == 0) return;
112 CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
113 if (pf == null) return;
114
115 float days = Global.getSector().getClock().convertToDays(amount);
116
117 if (Misc.getAbyssalDepth(pf) >= 1f) {
119 }
120
121 perCreatorTimeouts.advance(days);
122
123
124 sbTimeoutRemaining -= days;
125 if (sbTimeoutRemaining <= 0f) {
128 }
129
131 if (timeoutRemaining <= 0f) {
132 spawnGhost();
134 }
135
136 Iterator<SensorGhost> iter = ghosts.iterator();
137 while (iter.hasNext()) {
138 SensorGhost curr = iter.next();
139 curr.advance(amount);
140 if (curr.isDone()) {
141 iter.remove();
142 }
143 }
144 }
145
148 }
149
150 public void checkSensorBursts() {
151 if (!Global.getSector().getCurrentLocation().isHyperspace()) return;
152 if (timeoutRemaining < 1f) return;
153 if (Global.getSector().getMemoryWithoutUpdate().getBoolean(MemFlags.GLOBAL_SENSOR_BURST_JUST_USED_IN_CURRENT_LOCATION)) {
154 if (random.nextFloat() > SB_ATTRACT_GHOSTS_PROBABILITY) {
157 return;
158 }
159 CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
160 float range = 2000f;
161 for (CampaignFleetAPI fleet : Global.getSector().getCurrentLocation().getFleets()) {
162 float dist = Misc.getDistance(fleet.getLocation(), pf.getLocation());
163 if (dist > range) continue;
164 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.JUST_DID_SENSOR_BURST)) {
165 timeoutRemaining = 0.2f + 0.8f * random.nextFloat();
168 break;
169 }
170 }
171 }
172 }
173
174 public void spawnGhost() {
175 CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
176 boolean nearStream = Misc.isInsideSlipstream(pf.getLocation(), 1000f, pf.getContainingLocation());
177
178 boolean inAbyss = Misc.isInAbyss(pf);
179
180 WeightedRandomPicker<SensorGhostCreator> picker = new WeightedRandomPicker<SensorGhostCreator>(random);
181 for (SensorGhostCreator creator : CREATORS) {
182 if (perCreatorTimeouts.contains(creator.getId())) continue;
183 if (nearStream && !creator.canSpawnWhilePlayerInOrNearSlipstream()) continue;
184 if (inAbyss && !creator.canSpawnWhilePlayerInAbyss()) continue;
185 if (!inAbyss && !creator.canSpawnWhilePlayerOutsideAbyss()) continue;
186
187 float freq = creator.getFrequency(this);
188 picker.add(creator, freq);
189 }
190
191 SensorGhostCreator creator = picker.pick();
192 if (creator == null) return;
193
194 //System.out.println("Picked: " + creator.getId());
195
196 boolean canSpawn = true;
197 // important: the creator that can't spawn a ghost can still be picked, just won't fire
198 // otherwise moving in/out of slipstreams would manipulate ghost spawning
199 // can still manipulate it since it causes a "failed to create" timeout rather than a "created" one,
200 // but that should be a bit less noticeable
201// if (!creator.canSpawnWhilePlayerInOrNearSlipstream()) {
202// //CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
203// canSpawn = !Misc.isInsideSlipstream(pf.getLocation(), 1000f, pf.getContainingLocation());
204// }
205
206 List<SensorGhost> ghosts = null;
207 if (canSpawn) {
208 ghosts = creator.createGhost(this);
209 }
210 boolean anyFailed = false; // bit of a failsafe if a creator returns a failed-to-spawn ghost
211 if (ghosts != null) {
212 for (SensorGhost curr : ghosts) {
213 anyFailed |= curr.isCreationFailed();
214 curr.setDespawnInAbyss(!creator.canSpawnWhilePlayerInAbyss());
215 }
216 }
217 if (!canSpawn) {
218 anyFailed = true;
219 }
220
221 if (ghosts == null || ghosts.isEmpty() || anyFailed) {
222 float timeout = MIN_FAILED_CREATOR_TIMEOUT_DAYS +
224 perCreatorTimeouts.set(creator.getId(), timeout);
225 } else {
226 this.ghosts.addAll(ghosts);
227 if (random.nextFloat() < FULL_TIMEOUT_TRIGGER_PROBABILITY) {
230 } else {
233 }
234 perCreatorTimeouts.set(creator.getId(), creator.getTimeoutDaysOnSuccessfulCreate(this));
235 }
236 }
237
238
239 public boolean hasGhostOfClass(Class<?> clazz) {
240 for (SensorGhost ghost : ghosts) {
241 if (clazz.isInstance(ghost)) return true;
242 }
243 return false;
244 }
245
246 public Random getRandom() {
247 return random;
248 }
249
250 public boolean runWhilePaused() {
251 return false;
252 }
253
254 public boolean isDone() {
255 return false;
256 }
257
258 public List<SensorGhost> getGhosts() {
259 return ghosts;
260 }
261
262
263}
static SectorAPI getSector()
Definition Global.java:59
static SensorGhost getGhostFor(SectorEntityToken entity)
List< SensorGhost > createGhost(SensorGhostManager manager)
float getTimeoutDaysOnSuccessfulCreate(SensorGhostManager manager)