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