Starsector API
Loading...
Searching...
No Matches
PatrolFleetManager.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.fleets;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import org.apache.log4j.Logger;
7import org.lwjgl.util.vector.Vector2f;
8
9import com.fs.starfarer.api.EveryFrameScript;
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.campaign.BaseCampaignEventListener;
12import com.fs.starfarer.api.campaign.BattleAPI;
13import com.fs.starfarer.api.campaign.CampaignFleetAPI;
14import com.fs.starfarer.api.campaign.SectorEntityToken;
15import com.fs.starfarer.api.campaign.econ.MarketAPI;
16import com.fs.starfarer.api.impl.campaign.fleets.FleetFactory.PatrolType;
17import com.fs.starfarer.api.impl.campaign.ids.Conditions;
18import com.fs.starfarer.api.impl.campaign.ids.Factions;
19import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
20import com.fs.starfarer.api.impl.campaign.ids.Industries;
21import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
22import com.fs.starfarer.api.impl.campaign.ids.Ranks;
23import com.fs.starfarer.api.util.IntervalUtil;
24import com.fs.starfarer.api.util.Misc;
25import com.fs.starfarer.api.util.RollingAverageTracker;
26import com.fs.starfarer.api.util.WeightedRandomPicker;
27
28public class PatrolFleetManager extends BaseCampaignEventListener implements EveryFrameScript {
29
30 public static Logger log = Global.getLogger(PatrolFleetManager.class);
31
32 public static class PatrolFleetData {
33 public float startingFleetPoints = 0;
34 public CampaignFleetAPI fleet;
35 public PatrolType type;
36 public MarketAPI sourceMarket;
37 public PatrolFleetData(CampaignFleetAPI fleet, PatrolType type) {
38 this.fleet = fleet;
39 this.type = type;
40 }
41 }
42
43 //private float econInterval = Global.getSettings().getFloat("economyIntervalnGameDays");
44
45 private MarketAPI market;
46 private List<PatrolFleetData> activePatrols = new ArrayList<PatrolFleetData>();
47 private IntervalUtil tracker;
48 private int maxPatrols;
49
50 private RollingAverageTracker patrolBattlesLost;
51 public PatrolFleetManager(MarketAPI market) {
52 super(true);
53 this.market = market;
54
55 float interval = Global.getSettings().getFloat("averagePatrolSpawnInterval");
56 tracker = new IntervalUtil(interval * 0.75f, interval * 1.25f);
57
59 }
60
61 protected Object readResolve() {
62 if (patrolBattlesLost == null) {
63 float patrolStrengthCheckInterval = Global.getSettings().getFloat("economyIntervalnGameDays");
64 float min = patrolStrengthCheckInterval - Math.min(patrolStrengthCheckInterval * 0.5f, 2f);
65 float max = patrolStrengthCheckInterval + Math.min(patrolStrengthCheckInterval * 0.5f, 2f);
66 patrolBattlesLost = new RollingAverageTracker(min, max, Misc.getGenericRollingAverageFactor());
67 }
68 return this;
69 }
70
71 public void advance(float amount) {
72 //if (true) return;
73 float days = Global.getSector().getClock().convertToDays(amount);
74
75 patrolBattlesLost.advance(days);
76
77 float losses = patrolBattlesLost.getAverage();
78
79 //tracker.advance(days);
80 tracker.advance(days * Math.max(1f, losses));
81 if (!tracker.intervalElapsed()) return;
82
83 if (market.hasCondition(Conditions.DECIVILIZED)) return;
84
85 List<PatrolFleetData> remove = new ArrayList<PatrolFleetData>();
86 for (PatrolFleetData data : activePatrols) {
87 if (data.fleet.getContainingLocation() == null ||
88 !data.fleet.getContainingLocation().getFleets().contains(data.fleet)) {
89 remove.add(data);
90 log.info("Cleaning up orphaned patrol [" + data.fleet.getNameWithFaction() + "] for market [" + market.getName() + "]");
91 }
92 }
93 activePatrols.removeAll(remove);
94
95// if (market.getId().equals("jangala")) {
96// System.out.println("23rwefwe");
97// }
98 //maxPatrols = Math.max(1, market.getSize() - 3) + (int) (market.getStabilityValue() * 0.5f);
99 //float losses = patrolBattlesLost.getAverage();
100
101 maxPatrols = (int) (Math.max(1, market.getSize() - 3) * (market.getStabilityValue() / 10f)) +
102 (int) Math.max(0, Math.min(losses, 5));
103 if (maxPatrols < 1) maxPatrols = 1;
104
105 boolean hasStationOrSpaceport = market.hasIndustry(Industries.ORBITALSTATION) ||
106 market.hasSpaceport() ||
107 market.hasIndustry(Industries.BATTLESTATION);
108 if (market.hasIndustry(Industries.MILITARYBASE)) {
109 maxPatrols += 1;
110 if (hasStationOrSpaceport) maxPatrols++;
111 }
112 if (hasStationOrSpaceport) maxPatrols++;
113
114
115 //maxPatrols = 1;
116 log.debug("");
117 log.debug("Checking whether to spawn patrol for market [" + market.getName() + "]");
118 if (activePatrols.size() < maxPatrols) {
119 log.info(activePatrols.size() + " out of a maximum " + maxPatrols + " patrols in play for market [" + market.getName() + "]");
120
121 WeightedRandomPicker<PatrolType> picker = new WeightedRandomPicker<PatrolType>();
122 picker.add(PatrolType.FAST,
123 Math.max(1, maxPatrols - getCount(PatrolType.COMBAT, PatrolType.HEAVY)));
124 picker.add(PatrolType.COMBAT,
125 Math.max(1, maxPatrols - getCount(PatrolType.FAST, PatrolType.HEAVY) + market.getSize()) + losses);
126
127 if (market.getSize() >= 5) {
128 picker.add(PatrolType.HEAVY,
129 Math.max(1, maxPatrols - getCount(PatrolType.FAST, PatrolType.COMBAT) + market.getSize()) + losses * 2f);
130 }
131
132
133 PatrolType type = picker.pick();
134
135 CampaignFleetAPI fleet = createPatrolFleet(type, market, null, null, losses);
136 if (fleet == null) return;
137
138 SectorEntityToken entity = market.getPrimaryEntity();
139 entity.getContainingLocation().addEntity(fleet);
140 fleet.setLocation(entity.getLocation().x, entity.getLocation().y);
141
142 PatrolFleetData data = new PatrolFleetData(fleet, type);
143 data.startingFleetPoints = fleet.getFleetPoints();
144 data.sourceMarket = market;
145 activePatrols.add(data);
146
147 PatrolAssignmentAI ai = new PatrolAssignmentAI(fleet, data);
148 fleet.addScript(ai);
149
150 log.info("Spawned patrol fleet [" + fleet.getNameWithFaction() + "] from market " + market.getName());
151 } else {
152 log.debug("Maximum number of " + maxPatrols + " patrols already in play for market [" + market.getName() + "]");
153 }
154 }
155
156
157
158 public static CampaignFleetAPI createPatrolFleet(
159 PatrolType type, MarketAPI market, String factionId, Vector2f locInHyper, float losses) {
160
161// if (market.getId().equals("jangala")) {
162// System.out.println("wefwefwe");
163// }
164
165 float combat = 0f;
166 float tanker = 0f;
167 float freighter = 0f;
168 String fleetType = FleetTypes.PATROL_SMALL;
169 switch (type) {
170 case FAST:
171 fleetType = FleetTypes.PATROL_SMALL;
172 combat = Math.round(3f + (float) Math.random() * 2f);
173 combat += Math.min(5f, losses * 2f);
174 break;
175 case COMBAT:
176 fleetType = FleetTypes.PATROL_MEDIUM;
177 combat = Math.round(6f + (float) Math.random() * 3f);
178 combat += Math.min(25f, losses * 8f);
179
180 tanker = Math.round((float) Math.random());
181 break;
182 case HEAVY:
183 fleetType = FleetTypes.PATROL_LARGE;
184 combat = Math.round(10f + (float) Math.random() * 5f);
185 combat += Math.min(40f, losses * 12f);
186
187 tanker = 2f;
188 freighter = 2f;
189 break;
190 }
191 if (market != null) {
192 combat *= 1f + (market.getStabilityValue() / 20f);
193 } else {
194 combat *= 1.25f;
195 }
196
197 combat *= 4;
198 tanker *= 4;
199 freighter *= 4;
200
201 //combat += Math.min(30f, losses * 3f);
202 if (market != null && factionId == null) {
203 factionId = market.getFactionId();
204 }
205
206 FleetParamsV3 params = new FleetParamsV3(
207 market,
208 locInHyper,
209 factionId,
210 null,
211 fleetType,
212 combat, // combatPts
213 //5f + (float) Math.random() * 5f, // combatPts
214 freighter, // freighterPts
215 tanker, // tankerPts
216 0f, // transportPts
217 0f, // linerPts
218 0f, // utilityPts
219 0f // qualityMod
220 );
221// if (market != null && market.getId().equals("chicomoztoc")) {
222// System.out.println("wefwef");;;
223// }
224 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
225
226// CampaignFleetAPI fleet = FleetFactoryV2.createFleet(new FleetParams(
227// locInHyper,
228// market,
229// factionId,
230// null, // fleet's faction, if different from above, which is also used for source market picking
231// fleetType,
232// combat, // combatPts
233// freighter, // freighterPts
234// tanker, // tankerPts
235// 0f, // transportPts
236// 0f, // linerPts
237// 0f, // civilianPts
238// 0f, // utilityPts
239// 0f, // qualityBonus
240// -1f, // qualityOverride
241// 1f + Math.min(1f, losses / 10f), // officer num mult
242// 0 + (int) losses// officer level bonus
243// ));
244 if (fleet == null) return null;
245
246 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PATROL_FLEET, true);
247
248 if (type == PatrolType.FAST || type == PatrolType.COMBAT) {
249 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_CUSTOMS_INSPECTOR, true);
250 }
251
252 fleet.getCommander().setPostId(Ranks.POST_PATROL_COMMANDER);
253
254 switch (type) {
255 case FAST:
256 fleet.getCommander().setRankId(Ranks.SPACE_LIEUTENANT);
257 break;
258 case COMBAT:
259 fleet.getCommander().setRankId(Ranks.SPACE_COMMANDER);
260 break;
261 case HEAVY:
262 fleet.getCommander().setRankId(Ranks.SPACE_CAPTAIN);
263 break;
264 }
265
266 return fleet;
267 }
268
269
270 private int getCount(PatrolType ... types) {
271 int count = 0;
272 for (PatrolType type : types) {
273 for (PatrolFleetData data : activePatrols) {
274 if (data.type == type) count++;
275 }
276 }
277 return count;
278 }
279
280 public boolean isDone() {
281 return false;
282 }
283
284 public boolean runWhilePaused() {
285 return false;
286 }
287
288
289
290 @Override
291 public void reportFleetDespawned(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
292 super.reportFleetDespawned(fleet, reason, param);
293
294 for (PatrolFleetData data : activePatrols) {
295 if (data.fleet == fleet) {
296 activePatrols.remove(data);
297 break;
298 }
299 }
300 }
301
302 @Override
303 public void reportBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle) {
304 super.reportBattleOccurred(primaryWinner, battle);
305
306 boolean playerWon = battle.isPlayerSide(battle.getSideFor(primaryWinner));
307 boolean playerLost = battle.isPlayerSide(battle.getOtherSideFor(primaryWinner));
308 if (primaryWinner.isInOrNearSystem(market.getStarSystem())) {
309 // losing to pirates doesn't trigger patrol strength going up; don't want pirates wiped out
310 if (primaryWinner.getFaction().getId().equals(Factions.PIRATES)) return;
311 if (primaryWinner.getFaction().getId().equals(Factions.LUDDIC_PATH)) return;
312
313 for (CampaignFleetAPI loser : battle.getOtherSideSnapshotFor(primaryWinner)) {
314 if (loser.getFaction() == market.getFaction()) {
315 if (playerWon) {
316 patrolBattlesLost.add(1);
317 } else {
318 //patrolBattlesLost.add(1);
319 }
320 } else if (primaryWinner.getFaction() == market.getFaction()) {
321 // winning vs pirates doesn't trigger strength getting smaller, might happen too easily
322 if (loser.getFaction().getId().equals(Factions.PIRATES)) return;
323 if (loser.getFaction().getId().equals(Factions.LUDDIC_PATH)) return;
324 if (playerLost) {
325 patrolBattlesLost.sub(1);
326 } else {
327 //patrolBattlesLost.sub(1);
328 }
329 }
330 }
331 }
332 }
333
334
335
336
337
338}
339
340
341
342
343
344
345
346
347
348
349
350
351
352
static SettingsAPI getSettings()
Definition Global.java:51
static Logger getLogger(Class c)
Definition Global.java:26
static SectorAPI getSector()
Definition Global.java:59
static CampaignFleetAPI createFleet(FleetParamsV3 params)
void reportFleetDespawned(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param)
static CampaignFleetAPI createPatrolFleet(PatrolType type, MarketAPI market, String factionId, Vector2f locInHyper, float losses)
void reportBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle)