Starsector API
Loading...
Searching...
No Matches
EmergencyBurnAbilityAI.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.abilities.ai;
2
3import com.fs.starfarer.api.Global;
4import com.fs.starfarer.api.campaign.CampaignFleetAPI;
5import com.fs.starfarer.api.campaign.FleetAssignment;
6import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
7import com.fs.starfarer.api.campaign.ai.FleetAIFlags;
8import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
9import com.fs.starfarer.api.campaign.rules.MemoryAPI;
10import com.fs.starfarer.api.fleet.FleetMemberAPI;
11import com.fs.starfarer.api.impl.campaign.abilities.EmergencyBurnAbility;
12import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
13import com.fs.starfarer.api.util.IntervalUtil;
14import com.fs.starfarer.api.util.Misc;
15
17
18 public static String AI_USE_TIMEOUT_KEY = "$ebai_timeout";
19 public static float AI_USE_TIMEOUT_DAYS_MIN = 3f;
20 public static float AI_USE_TIMEOUT_DAYS_MAX = 5f;
21
22 public static float AI_FREQUENCY_MULT = 1f;
23
24 protected IntervalUtil interval = new IntervalUtil(0.05f, 0.15f);
25
26// public EmergencyBurnAbilityAI(AbilityPlugin ability, ModularFleetAIAPI ai) {
27// super(ability, ai);
28// }
29
30 protected void activate() {
31// if (fleet.getContainingLocation() != null && fleet.getContainingLocation().isCurrentLocation()) {
32// float dist = Misc.getDistance(fleet.getLocation(), Global.getSector().getPlayerFleet().getLocation());
33// if (dist < 1000f) {
34// System.out.println("fwefewfew");
35// }
36// }
39 mem.set(AI_USE_TIMEOUT_KEY, true,
41 }
42
43 public void advance(float days) {
45 if (!interval.intervalElapsed()) return;
46
47// if (fleet.getName().contains("[5]")) {
48// System.out.println("ewfwefwe");
49// }
52 mem.set(FleetAIFlags.HAS_SPEED_BONUS, true, 0.2f);
53 mem.set(FleetAIFlags.HAS_VISION_PENALTY, true, 0.2f);
54 return;
55 }
56
57 // max burn bonus wouldn't mean much due to a low multiplier, don't use it
58 // DO NOT want to check HAS_SPEED_PENALTY here, as using this ability will cancel "Go Dark".
59 // since EB now removes terrain penalties
60 // but a *very* low mult can also be indicative of an interdict...
61 //if (fleet.getStats().getFleetwideMaxBurnMod().getBonusMult() <= 0.3f) {
63 return;
64 }
65
67 return;
68 }
69
71// if (fleet.isInCurrentLocation()) {
72// System.out.println("23r23r23r3");
73// }
75 return;
76 }
77
79 activate();
80 return;
81 }
82
85 activate();
86 return;
87 }
88
89 if (Misc.isInsideBlackHole(fleet, false)) {
90 activate();
91 return;
92 }
93
94
95
98 //Vector2f travelDest = mem.getVector2f(FleetAIFlags.TRAVEL_DEST);
99
100 // need to evaluate whether ability is worth using: how desperate the situation is vs the CR hit
101
102 // being pursued by a faster enemy that's relatively close: turn on
103 if (fleeingFrom != null) {
104 if (fleeingFrom.isStationMode()) return;
105
106
107 VisibilityLevel level = fleet.getVisibilityLevelTo(fleeingFrom);
108 if (level == VisibilityLevel.NONE) return; // they can't see us, don't make it easier
109
110 if (!ability.isUsable()) return;
111
112 if (fleeingFrom.isPlayerFleet()) {
113 boolean avoidingPlayer = Misc.isAvoidingPlayerHalfheartedly(fleet);
114 if (avoidingPlayer) return;
115 }
116
117 UseCost cost = getUseCost();
118 boolean hopelessFight = isGreatlyOutmatchedBy(fleeingFrom);
119 float dist = Misc.getDistance(fleet.getLocation(), fleeingFrom.getLocation()) - fleet.getRadius() + fleeingFrom.getRadius();
120 float detRange = fleeingFrom.getMaxSensorRangeToDetect(fleet);
121 float ourSpeed = fleet.getFleetData().getBurnLevel();
122 float theirSpeed = fleeingFrom.getFleetData().getBurnLevel();
123 float closingSpeed = Misc.getClosingSpeed(fleet.getLocation(), fleeingFrom.getLocation(),
124 fleet.getVelocity(), fleeingFrom.getVelocity());
125 if ((theirSpeed > ourSpeed && closingSpeed > 1) || (closingSpeed > 1 && dist < 100)) {
126 if (hopelessFight && dist < 200) { // very close and really don't want to fight
127 activate();
128 } else if ((cost == UseCost.LOW || cost == UseCost.MEDIUM) && dist < 500) { // low cost, getting decently close
129 activate();
130 } else if ((cost == UseCost.LOW || cost == UseCost.MEDIUM) && dist < 100) { // medium cost, very close
131 activate();
132 } else if ((cost == UseCost.LOW || cost == UseCost.MEDIUM) && dist > detRange - 100f) { // low cost, close to being able to get out of sight
133 activate();
134 }
135 }
136 return;
137 }
138
139 // pursuing a faster enemy, and would be faster then them with EB on: turn on
140 if (pursueTarget != null) {
141 if (pursueTarget.isStationMode()) return;
142
143 if (fleet.getAI() instanceof ModularFleetAIAPI) {
146 return;
147 }
148 }
149
150 VisibilityLevel level = pursueTarget.getVisibilityLevelTo(fleet);
151 if (level == VisibilityLevel.NONE) return;
152
153 if (pursueTarget.isPlayerFleet()) {
154 level = fleet.getVisibilityLevelTo(pursueTarget);
155 if (level == VisibilityLevel.NONE) {
156 float closingSpeed = Misc.getClosingSpeed(pursueTarget.getLocation(), fleet.getLocation(),
157 pursueTarget.getVelocity(), fleet.getVelocity());
158 if (closingSpeed > 0) {
159 return;
160 }
161 }
162 }
163
164
165 if (!ability.isUsable()) return;
166
167 boolean targetInsignificant = otherInsignificant(pursueTarget);// && !pursueTarget.isPlayerFleet();
168// if (pursueTarget.isPlayerFleet()) {
169// System.out.println("test player fleet EB");
170// }
171
172 UseCost cost = getUseCost();
173 float dist = Misc.getDistance(fleet.getLocation(), pursueTarget.getLocation()) - fleet.getRadius() - pursueTarget.getRadius();
174 if (dist < 0) return;
175
176 float detRange = pursueTarget.getMaxSensorRangeToDetect(fleet);
177 float ourSpeed = fleet.getFleetData().getBurnLevel();
178 float theirSpeed = pursueTarget.getFleetData().getBurnLevel();
179
180 float closingSpeed = Misc.getClosingSpeed(fleet.getLocation(), pursueTarget.getLocation(),
181 fleet.getVelocity(), pursueTarget.getVelocity());
182
183 if (cost == UseCost.LOW && closingSpeed <= -1 && dist > detRange - 100f) { // about to lose sensor contact
184 activate();
185 } else if (cost == UseCost.LOW && dist < 200 && closingSpeed < 50 && !targetInsignificant) { // close, pounce
186 activate();
187 } else if (cost == UseCost.LOW && theirSpeed > ourSpeed && dist > 300 && !targetInsignificant) {
188 activate();
189 }
190 return;
191 }
192
193
194 boolean useEB = mem.getBoolean(FleetAIFlags.USE_EB_FOR_TRAVEL);
195 if (useEB) {
196 if (!ability.isUsable()) return;
197 activate();
198 return;
199 }
200
201 }
202
203 public static enum UseCost {
204 LOW,
205 MEDIUM,
206 HIGH
207 }
208 private UseCost getUseCost() {
209 float count = 0;
210 float numCritAlready = 0;
211 float numCrit = 0;
212 float numLow = 0;
213 float numOk = 0;
214
216 float crLow = Global.getSettings().getCRPlugin().getMalfunctionThreshold(null) + 0.01f;
217
218 boolean allCRMaxed = true;
220 count++;
221
222 if (member.isCivilian()) {
223 numOk++;
224 continue;
225 }
226
227 float useCost = member.getDeployCost() * EmergencyBurnAbility.CR_COST_MULT;
228 float cr = member.getRepairTracker().getCR();
229 float maxCR = member.getRepairTracker().getMaxCR();
230
231 float crAfter = cr - useCost;
232
233 if (cr < maxCR) {
234 allCRMaxed = false;
235 }
236
237 if (cr <= crCrit * 0.5f) {
238 numCritAlready++;
239 }
240 if (crAfter <= crCrit) {
241 numCrit++;
242 } else if (crAfter <= crLow) {
243 numLow++;
244 } else {
245 numOk++;
246 }
247 }
248
249 if (numCritAlready >= count) return UseCost.LOW;
250
251 if (allCRMaxed) return UseCost.LOW;
252 if (numOk + numLow >= count) return UseCost.MEDIUM;
253 //if (numOk + numLow >= count && numOk * 0.5f >= numLow) return UseCost.LOW;
254 //if (numLow > numCrit * 0.5f) return UseCost.MEDIUM;
255 return UseCost.HIGH;
256 }
257
258
259
260
261 protected boolean isGreatlyOutmatchedBy(CampaignFleetAPI other) {
262 float us = getStrength(fleet);
263 float them = getStrength(other);
264
265 if (us < 0.1f) us = 0.1f;
266 if (them < 0.1f) them = 0.1f;
267 return them > us * 3f;
268 }
269
270 protected boolean otherInsignificant(CampaignFleetAPI other) {
271 float us = getStrength(fleet);
272 float them = getStrength(other);
273
274 if (us < 0.1f) us = 0.1f;
275 if (them < 0.1f) them = 0.1f;
276 return us > them * 5f;
277 }
278
279 public static float getStrength(CampaignFleetAPI fleet) {
280 float str = 0f;
282 if (member.canBeDeployedForCombat()) {
283 float strength = member.getMemberStrength();
284 str += strength;
285 }
286 }
287 return str;
288 }
289}
290
291
292
293
294
295
static SettingsAPI getSettings()
Definition Global.java:57
static boolean isInsideSlipstream(Vector2f loc, float radius)
Definition Misc.java:6408
static boolean isInsideBlackHole(CampaignFleetAPI fleet, boolean includeEventHorizon)
Definition Misc.java:2359
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static float getClosingSpeed(Vector2f p1, Vector2f p2, Vector2f v1, Vector2f v2)
Definition Misc.java:1354
static boolean isAvoidingPlayerHalfheartedly(CampaignFleetAPI fleet)
Definition Misc.java:6128
static boolean isSlowMoving(CampaignFleetAPI fleet)
Definition Misc.java:5651
CombatReadinessPlugin getCRPlugin()
List< FleetMemberAPI > getMembersListCopy()
VisibilityLevel getVisibilityLevelTo(SectorEntityToken other)
float getMaxSensorRangeToDetect(SectorEntityToken other)
void set(String key, Object value)
CampaignFleetAPI getFleet(String key)
float getCriticalMalfunctionThreshold(MutableShipStatsAPI stats)
float getMalfunctionThreshold(MutableShipStatsAPI stats)