Starsector API
Loading...
Searching...
No Matches
BattleAutoresolverPluginImpl.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6
7import com.fs.starfarer.api.Global;
8import com.fs.starfarer.api.campaign.BattleAPI;
9import com.fs.starfarer.api.campaign.BattleAutoresolverPlugin;
10import com.fs.starfarer.api.campaign.CampaignFleetAPI;
11import com.fs.starfarer.api.campaign.CombatDamageData;
12import com.fs.starfarer.api.campaign.EngagementResultForFleetAPI;
13import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin;
14import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.EncounterOption;
15import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
16import com.fs.starfarer.api.combat.EngagementResultAPI;
17import com.fs.starfarer.api.combat.MutableShipStatsAPI;
18import com.fs.starfarer.api.combat.ShieldAPI.ShieldType;
19import com.fs.starfarer.api.combat.ShipHullSpecAPI;
20import com.fs.starfarer.api.combat.ShipVariantAPI;
21import com.fs.starfarer.api.fleet.FleetGoal;
22import com.fs.starfarer.api.fleet.FleetMemberAPI;
23import com.fs.starfarer.api.impl.campaign.ids.HullMods;
24import com.fs.starfarer.api.loading.WeaponSlotAPI;
25import com.fs.starfarer.api.util.Misc;
26import com.fs.starfarer.api.util.WeightedRandomPicker;
27
28public class BattleAutoresolverPluginImpl implements BattleAutoresolverPlugin {
29
30 public static class EngagementResultImpl implements EngagementResultAPI {
31 public BattleAPI battle;
32 public EngagementResultForFleetImpl winnerResult, loserResult;
33
34 public EngagementResultImpl(BattleAPI battle, CampaignFleetAPI winner, CampaignFleetAPI loser) {
35 this.battle = battle;
36 winnerResult = new EngagementResultForFleetImpl(winner);
37 loserResult = new EngagementResultForFleetImpl(loser);
38 }
39
40 public BattleAPI getBattle() {
41 return battle;
42 }
43
44 public boolean didPlayerWin() {
45 //return winnerResult.getFleet() != null && winnerResult.getFleet().isPlayerFleet();
46 return winnerResult.getFleet() != null && Misc.isPlayerOrCombinedContainingPlayer(winnerResult.getFleet());
47 }
48
49 public EngagementResultForFleetAPI getLoserResult() {
50 return loserResult;
51 }
52
53 public EngagementResultForFleetAPI getWinnerResult() {
54 return winnerResult;
55 }
56
57 public boolean isPlayerOutBeforeEnd() {
58 return false;
59 }
60
61 public void setPlayerOutBeforeEnd(boolean playerOutBeforeEnd) {
62 }
63
64 public void setBattle(BattleAPI battle) {
65 this.battle = battle;
66 }
67
68 public CombatDamageData getLastCombatDamageData() {
69 return null;
70 }
71
72 public void setLastCombatDamageData(CombatDamageData lastCombatData) {
73
74 }
75 }
76
77 public static class EngagementResultForFleetImpl implements EngagementResultForFleetAPI {
78 public CampaignFleetAPI fleet;
79 public FleetGoal goal;
80 public boolean winner = false;
81 public List<FleetMemberAPI> deployed = new ArrayList<FleetMemberAPI>();
82 public List<FleetMemberAPI> reserves = new ArrayList<FleetMemberAPI>();
83 public List<FleetMemberAPI> destroyed = new ArrayList<FleetMemberAPI>();
84 public List<FleetMemberAPI> disabled = new ArrayList<FleetMemberAPI>();
85 public List<FleetMemberAPI> retreated = new ArrayList<FleetMemberAPI>();
86
87 public EngagementResultForFleetImpl(CampaignFleetAPI fleet) {
88 this.fleet = fleet;
89 }
90
91 public List<FleetMemberAPI> getDeployed() {
92 return deployed;
93 }
94 public List<FleetMemberAPI> getDestroyed() {
95 return destroyed;
96 }
97 public List<FleetMemberAPI> getDisabled() {
98 return disabled;
99 }
100 public CampaignFleetAPI getFleet() {
101 return fleet;
102 }
103 public FleetGoal getGoal() {
104 return goal;
105 }
106 public List<FleetMemberAPI> getReserves() {
107 return reserves;
108 }
109 public List<FleetMemberAPI> getRetreated() {
110 return retreated;
111 }
112 public List<DeployedFleetMemberAPI> getAllEverDeployedCopy() {
113 return null;
114 }
115 public boolean isWinner() {
116 return winner;
117 }
118 public void setWinner(boolean winner) {
119 this.winner = winner;
120 }
121 public void resetAllEverDeployed() {
122 }
123 public void setGoal(FleetGoal goal) {
124 this.goal = goal;
125 }
126 public boolean isPlayer() {
127 return false;
128 }
129
130 public boolean enemyCanCleanDisengage() {
131 // TODO Auto-generated method stub
132 return false;
133 }
134 }
135
136 public static enum FleetMemberBattleOutcome {
137 UNSCATHED,
138 LIGHT_DAMAGE,
139 MEDIUM_DAMAGE,
140 HEAVY_DAMAGE,
141 DISABLED,
142 }
143
144 public static class FleetMemberAutoresolveData {
145 public FleetMemberAPI member;
146 public float strength;
147 public float shieldRatio;
148 public boolean combatReady;
149 }
150
151 public static class FleetAutoresolveData {
152 public CampaignFleetAPI fleet;
153 public float fightingStrength;
154 public List<FleetMemberAutoresolveData> members = new ArrayList<FleetMemberAutoresolveData>();
155
156 public void report() {
157 if (!report) return;
158
159 BattleAutoresolverPluginImpl.report(String.format("Fighting srength of %s: %f", fleet.getNameWithFaction(), fightingStrength));
160 for (FleetMemberAutoresolveData data : members) {
161 String str = String.format("%40s: CR % 3d%% FP % 4d STR % 3f Shield %3.2f",
162 data.member.getVariant().getFullDesignationWithHullName(),
163 (int)(data.member.getRepairTracker().getCR() * 100f),
164 data.member.getFleetPointCost(),
165 data.strength,
166 data.shieldRatio);
168 }
169
170 }
171 }
172
173 protected CampaignFleetAPI one;
174 protected CampaignFleetAPI two;
175 protected final BattleAPI battle;
176
177 protected boolean playerPursuitAutoresolveMode = false;
178 protected List<FleetMemberAPI> playerShipsToDeploy;
179
181 this.battle = battle;
182
183 one = battle.getCombinedOne();
184 two = battle.getCombinedTwo();
185 if (battle.isPlayerInvolved()) {
186 one = battle.getPlayerCombined();
187 two = battle.getNonPlayerCombined();
188 }
189
191 setReport(false);
192 }
193
194 public void resolve() {
195 // figure out battle type (escape vs engagement)
196
197 report("***");
198 report("***");
199 report(String.format("Autoresolving %s vs %s", one.getNameWithFaction(), two.getNameWithFaction()));
200
204 EncounterOption optionOne = one.getAI().pickEncounterOption(context, two);
205 EncounterOption optionTwo = two.getAI().pickEncounterOption(context, one);
206
207 if (optionOne == EncounterOption.DISENGAGE && optionTwo == EncounterOption.DISENGAGE) {
208 report("Both fleets want to disengage");
209 report("Finished autoresolving engagement");
210 report("***");
211 report("***");
212 return;
213 }
214
215 boolean oneEscaping = false;
216 boolean twoEscaping = false;
217
218 boolean freeDisengageIfCanOutrun = false;
219
220 if (optionOne == EncounterOption.DISENGAGE && optionTwo == EncounterOption.ENGAGE) {
221 report(String.format("%s wants to disengage", one.getNameWithFaction()));
222 oneEscaping = true;
223 if (freeDisengageIfCanOutrun && context.canOutrunOtherFleet(one, two)) {
224 report(String.format("%s can outrun other fleet", one.getNameWithFaction()));
225 report("Finished autoresolving engagement");
226 report("***");
227 report("***");
228 return;
229 }
230 }
231 if (optionOne == EncounterOption.ENGAGE && optionTwo == EncounterOption.DISENGAGE) {
232 report(String.format("%s wants to disengage", two.getNameWithFaction()));
233 twoEscaping = true;
234 if (freeDisengageIfCanOutrun && context.canOutrunOtherFleet(two, one)) {
235 report(String.format("%s can outrun other fleet", two.getNameWithFaction()));
236 report("Finished autoresolving engagement");
237 report("***");
238 report("***");
239 return;
240 }
241 }
242
243 resolveEngagement(context, oneEscaping, twoEscaping);
244
245 report("");
246 report("Finished autoresolving engagement");
247 report("***");
248 report("***");
249 }
250
251
253 this.context = context;
255 this.playerShipsToDeploy = playerShipsToDeploy;
256 resolveEngagement(context, false, true);
257 }
258
259 protected void resolveEngagement(FleetEncounterContext context, boolean oneEscaping, boolean twoEscaping) {
260
261 FleetAutoresolveData dataOne = computeDataForFleet(one);
262 FleetAutoresolveData dataTwo = computeDataForFleet(two);
263
264 if (dataOne.fightingStrength <= 0 && dataTwo.fightingStrength <= 0) {
265 return;
266 }
267 if (dataOne.fightingStrength <= 0.1f) {
268 dataOne.fightingStrength = 0.1f;
269 }
270 if (dataTwo.fightingStrength <= 0.1f) {
271 dataTwo.fightingStrength = 0.1f;
272 }
273
274 FleetAutoresolveData winner, loser;
275
276 report("");
277 report("--------------------------------------------");
278 dataOne.report();
279
280 report("");
281 report("--------------------------------------------");
282 dataTwo.report();
283
284 report("");
285 report("");
286
287 boolean loserEscaping = false;
288 if ((dataOne.fightingStrength > dataTwo.fightingStrength || twoEscaping) && !oneEscaping) {
289 report(String.format("%s won engagement", one.getNameWithFaction()));
290 winner = dataOne;
291 loser = dataTwo;
292 if (twoEscaping) {
293 loserEscaping = true;
294 }
295 } else {
296 report(String.format("%s won engagement", two.getNameWithFaction()));
297 winner = dataTwo;
298 loser = dataOne;
299 if (oneEscaping) {
300 loserEscaping = true;
301 }
302 }
303
304 float winnerAdvantage = winner.fightingStrength / loser.fightingStrength;
305// if (winnerAdvantage > 2f) winnerAdvantage = 2f;
306// if (winnerAdvantage < 0.5f) winnerAdvantage = 0.5f;
307 if (winnerAdvantage > 10f) winnerAdvantage = 10f;
308 if (winnerAdvantage < 0.1f) winnerAdvantage = 0.1f;
309 //if (winnerAdvantage < 0.1f) winnerAdvantage = 0.1f;
310
311 float damageDealtToWinner = loser.fightingStrength / winnerAdvantage;
312 float damageDealtToLoser = winner.fightingStrength * winnerAdvantage;
314 damageDealtToWinner = 0f;
315 }
316
317 float damMult = Global.getSettings().getFloat("autoresolveDamageMult");
318 damageDealtToWinner *= damMult;
319 damageDealtToLoser *= damMult;
320
321 //result = new EngagementResultImpl(winner.fleet, loser.fleet);
322 result = new EngagementResultImpl(context.getBattle(),
323 context.getBattle().getCombinedFor(winner.fleet),
324 context.getBattle().getCombinedFor(loser.fleet));
325
326 report("");
327 report("Applying damage to loser's ships");
328 report("--------------------------------------------");
329 Collections.shuffle(loser.members);
330 //boolean loserCarrierLeft = false;
331 for (FleetMemberAutoresolveData data : loser.members) {
332 report(String.format("Remaining damage to loser: %02.2f", damageDealtToLoser));
333 FleetMemberBattleOutcome outcome = computeOutcomeForFleetMember(data, 1f/winnerAdvantage, damageDealtToLoser, loserEscaping, false);
334 damageDealtToLoser -= data.strength;
335 if (damageDealtToLoser < 0) damageDealtToLoser = 0;
336 }
337
338 for (FleetMemberAutoresolveData data : loser.members) {
339 if (data.member.getStatus().getHullFraction() > 0) { // || (data.member.isFighterWing() && loserCarrierLeft)) {
340 result.getLoserResult().getRetreated().add(data.member);
341 } else {
342 result.getLoserResult().getDisabled().add(data.member);
343 }
344 }
345
346
347 report("");
348 report("Applying damage to winner's ships");
349 report("--------------------------------------------");
350 Collections.shuffle(winner.members);
351
352 boolean winnerCarrierLeft = false;
353 for (FleetMemberAutoresolveData data : winner.members) {
354 if (!data.combatReady) continue;
355 report(String.format("Remaining damage to winner: %02.2f", damageDealtToWinner));
356 FleetMemberBattleOutcome outcome = computeOutcomeForFleetMember(data, winnerAdvantage, damageDealtToWinner, false, loserEscaping);
357 damageDealtToWinner -= data.strength;
358 if (damageDealtToWinner < 0) damageDealtToWinner = 0;
359
360 if (data.member.isMothballed()) continue;
361 if (data.member.getStatus().getHullFraction() > 0 && data.member.getNumFlightDecks() > 0) {
362 winnerCarrierLeft = true;
363 }
364 }
365
366 // which ships should count as "deployed" for CR loss purposes?
367 // anything that was disabled, and then anything up to double the loser's strength
368 float deployedStrength = 0f;
369 float maxDeployedStrength = loser.fightingStrength * 2f;
370 for (FleetMemberAutoresolveData data : winner.members) {
371 if (!(data.member.getStatus().getHullFraction() > 0 || (data.member.isFighterWing() && winnerCarrierLeft))) {
372 deployedStrength += data.strength;
373 }
374 }
375
376 for (FleetMemberAutoresolveData data : winner.members) {
378 if (playerShipsToDeploy.contains(data.member) || data.member.isAlly()) {
379 result.getWinnerResult().getDeployed().add(data.member);
380 } else {
381 result.getWinnerResult().getReserves().add(data.member);
382 }
383 } else {
384 if (data.member.getStatus().getHullFraction() > 0) {
385 if (deployedStrength < maxDeployedStrength) {
386 result.getWinnerResult().getDeployed().add(data.member);
387 deployedStrength += data.strength;
388 } else {
389 result.getWinnerResult().getReserves().add(data.member);
390 }
391 } else {
392 result.getWinnerResult().getDisabled().add(data.member);
393 }
394 }
395 }
396
397
398 // CR hit, ship/crew losses get applied here
399 ((EngagementResultForFleetImpl)result.getWinnerResult()).setGoal(FleetGoal.ATTACK);
400 ((EngagementResultForFleetImpl)result.getWinnerResult()).setWinner(true);
401
402 if (loserEscaping) {
403 ((EngagementResultForFleetImpl)result.getLoserResult()).setGoal(FleetGoal.ESCAPE);
404 } else {
405 ((EngagementResultForFleetImpl)result.getLoserResult()).setGoal(FleetGoal.ATTACK);
406 }
407 ((EngagementResultForFleetImpl)result.getLoserResult()).setWinner(false);
408
409
410 // will be handled inside the interaction dialog if it's the player auto-resolving pursuit
413 //context.applyPostEngagementOption(result);
415
416 // need to set up one fleet as attacking, one fleet as losing + escaping/disengaging
417 // for the scrapping/looting/etc to work properly
418 context.getDataFor(winner.fleet).setDisengaged(false);
419 context.getDataFor(winner.fleet).setWonLastEngagement(true);
420 context.getDataFor(winner.fleet).setLastGoal(FleetGoal.ATTACK);
421
422 context.getDataFor(loser.fleet).setDisengaged(true);
423 context.getDataFor(loser.fleet).setWonLastEngagement(false);
424 context.getDataFor(loser.fleet).setLastGoal(FleetGoal.ESCAPE);
425
426 if (!winner.fleet.isAIMode()) {
427 context.generateLoot(null, true);
429 context.recoverCrew(winner.fleet);
430 }
431 //context.repairShips();
432
434 } else {
435 // for ship recovery to recognize these ships as non-player
436// DataForEncounterSide data = context.getDataFor(loser.fleet);
437// for (FleetMemberAPI member : data.getDisabledInLastEngagement()) {
438// member.setOwner(1);
439// }
440// for (FleetMemberAPI member : data.getDestroyedInLastEngagement()) {
441// member.setOwner(1);
442// }
443 for (FleetMemberAutoresolveData data : loser.members) {
444 data.member.setOwner(1);
445 }
446 }
447
448// context.getBattle().uncombine();
449// context.getBattle().finish();
450 }
451
452
453 public static void applyDamageToFleetMember(FleetMemberAPI member,
454 float hullFraction) {
455 if (member.isFighterWing()) return;
456 if (hullFraction <= 0) return;
457
458 float num = member.getStatus().getNumStatuses();
459 boolean someActiveRemaining = false;
460 for (int i = 0; i < num; i++) {
461 ShipVariantAPI variant = member.getVariant();
462 if (i > 0) {
463 String slotId = member.getVariant().getModuleSlots().get(i - 1);
464 variant = variant.getModuleVariant(slotId);
465 }
466
467 if (variant.hasHullMod(HullMods.VASTBULK)) {
468 float dam = Math.min(hullFraction, 0.9f);
469 float hits = Math.min(5f, dam / 0.1f);
470 if (hits < 1) hits = 1;
471 //hits = 10;
472 for (int j = 0; j < hits; j++) {
473 member.getStatus().applyHullFractionDamage(dam / hits, i);
474 member.getStatus().setHullFraction(i, 1f);
475 }
476 continue;
477 }
478
479 if (i > 0 && !Misc.isActiveModule(variant)) continue;
480
481 float damageMult = 1f;
482 if (i > 0) {
483 damageMult = (float) Math.random();
484 damageMult *= 2f;
485 }
486
487 float damage = hullFraction * damageMult;
488 if (damage <= 0) continue;
489
490 member.getStatus().applyHullFractionDamage(damage, i);
491
492 float hits = Math.min(5f, damage / 0.1f);
493 if (hits < 1) hits = 1;
494 for (int j = 0; j < hits; j++) {
495 member.getStatus().applyHullFractionDamage((damage / hits) + 0.001f, i);
496 }
497
498 if (i > 0 && member.getStatus().getHullFraction(i) <= 0) {
499 member.getStatus().setDetached(i, true);
500 }
501
502 if (Misc.isActiveModule(variant) && (!member.getStatus().isDetached(i) || member.getStatus().getHullFraction(i) > 0)) {
503 someActiveRemaining = true;
504 }
505 }
506
507 if (num > 1) {
508 float farthestDetached = 0;
509 for (int i = 1; i < num; i++) {
510 ShipVariantAPI variant = member.getVariant();
511 if (member.getStatus().isDetached(i)) {
512 String slotId = variant.getModuleSlots().get(i - 1);
513 ShipVariantAPI mv = member.getVariant().getModuleVariant(slotId);
514 if (!Misc.isActiveModule(mv)) continue;
515
516 WeaponSlotAPI slot = variant.getHullSpec().getWeaponSlotAPI(slotId);
517 float dist = slot.getLocation().length();
518 if (dist > farthestDetached) {
519 farthestDetached = dist;
520 }
521 }
522 }
523
524 for (int i = 1; i < num; i++) {
525 ShipVariantAPI variant = member.getVariant();
526 if (!member.getStatus().isDetached(i)) {
527 String slotId = variant.getModuleSlots().get(i - 1);
528 ShipVariantAPI mv = member.getVariant().getModuleVariant(slotId);
529 if (mv.hasHullMod(HullMods.VASTBULK)) continue;
530 if (!Misc.isActiveModule(mv)) {
531 WeaponSlotAPI slot = variant.getHullSpec().getWeaponSlotAPI(slotId);
532 float dist = slot.getLocation().length();
533 if (dist <= farthestDetached + 200f) {
534 member.getStatus().setHullFraction(i, 0f);
535 member.getStatus().setDetached(i, true);
536 }
537 }
538 }
539 }
540 }
541
542
543 if (!someActiveRemaining || hullFraction >= 1f) {
544 for (int i = 0; i < num; i++) {
545 member.getStatus().setHullFraction(i, 0f);
546 if (i > 0) {
547 member.getStatus().setDetached(i, true);
548 }
549 }
550 }
551 }
552
553
554 protected FleetMemberBattleOutcome computeOutcomeForFleetMember(FleetMemberAutoresolveData data, float advantageInBattle,
555 float maxDamage, boolean escaping, boolean enemyEscaping) {
556 ShipHullSpecAPI hullSpec = data.member.getHullSpec();
557
558 float unscathed = 1f;
559 float lightDamage = 0f;
560 float mediumDamage = 0f;
561 float heavyDamage = 0f;
562 float disabled = 0f;
563
564 switch (hullSpec.getHullSize()) {
565 case CAPITAL_SHIP:
566 unscathed = 5f;
567 break;
568 case CRUISER:
569 unscathed = 10f;
570 break;
571 case DESTROYER:
572 unscathed = 15;;
573 break;
574 case FRIGATE:
575 case FIGHTER:
576 unscathed = 30f;
577 break;
578 }
579
580 float maxDamageRatio = maxDamage / data.strength;
581 if (maxDamageRatio > 1) maxDamageRatio = 1;
582 if (maxDamageRatio <= 0) maxDamageRatio = 0;
583
584 if (maxDamageRatio >= 0.8f) {
585 disabled = 20f;
586 heavyDamage = 10f;
587 mediumDamage = 10f;
588 lightDamage = 5f;
589 } else if (maxDamageRatio >= 0.6f) {
590 disabled = 5f;
591 heavyDamage = 20f;
592 mediumDamage = 10f;
593 lightDamage = 5f;
594 } else if (maxDamageRatio >= 0.4f) {
595 disabled = 0f;
596 heavyDamage = 10f;
597 mediumDamage = 20f;
598 lightDamage = 10f;
599 } else if (maxDamageRatio >= 0.2f) {
600 disabled = 0f;
601 heavyDamage = 0f;
602 mediumDamage = 10f;
603 lightDamage = 20f;
604 } else if (maxDamageRatio > 0) {
605 disabled = 0f;
606 heavyDamage = 0f;
607 mediumDamage = 5f;
608 lightDamage = 10f;
609 }
610
611 if (escaping) {
612 unscathed *= 2f;
613 lightDamage *= 1.5f;
614 }
615
616 if (enemyEscaping) {
617 disabled *= 0.5f;
618 heavyDamage *= 0.6f;
619 mediumDamage *= 0.7f;
620 lightDamage *= 0.8f;
621 unscathed *= 1f;
622 }
623
624 // advantageInBattle goes from 0.5 (bad) to 2 (good)
625 unscathed *= advantageInBattle;
626 lightDamage *= advantageInBattle;
627
628 float shieldRatio = data.shieldRatio;
629
630 // shieldRatio goes from 0 (no shields/no flux) to 1 (shields dominate hull/armor)
631 // shieldRatio at 0.5 roughly indicates balanced shields and hull/armor effectiveness
632
633 disabled *= 1.5f - shieldRatio * 1f;
634 heavyDamage *= 1.4f - shieldRatio * 0.8f;
635 mediumDamage *= 1.3f - shieldRatio * 0.6f;
636 lightDamage *= 1.2f - shieldRatio * 0.4f;
637 unscathed *= 0.9f + shieldRatio * 0.2f;
638
639
640 if (data.member.isStation()) {
641 heavyDamage += disabled;
642 disabled = 0f; // only disabled when heavy damage takes out all modules
643 }
644
645
646 WeightedRandomPicker<FleetMemberBattleOutcome> picker = new WeightedRandomPicker<FleetMemberBattleOutcome>();
647
648 picker.add(FleetMemberBattleOutcome.DISABLED, disabled);
649 picker.add(FleetMemberBattleOutcome.HEAVY_DAMAGE, heavyDamage);
650 picker.add(FleetMemberBattleOutcome.MEDIUM_DAMAGE, mediumDamage);
651 picker.add(FleetMemberBattleOutcome.LIGHT_DAMAGE, lightDamage);
652 picker.add(FleetMemberBattleOutcome.UNSCATHED, unscathed);
653
654
655 report(String.format("Disabled: %d, Heavy: %d, Medium: %d, Light: %d, Unscathed: %d (Shield ratio: %3.2f)",
656 (int) disabled, (int) heavyDamage, (int) mediumDamage, (int) lightDamage, (int) unscathed, shieldRatio));
657
658 FleetMemberBattleOutcome outcome = picker.pick();
659
660
661 float damage = 0f;
662
663 data.member.getStatus().resetDamageTaken();
664
665
666 switch (outcome) {
667 case DISABLED:
668 report(String.format("%40s: disabled", data.member.getVariant().getFullDesignationWithHullName()));
669 damage = 1f;
670 break;
671 case HEAVY_DAMAGE:
672 report(String.format("%40s: heavy damage", data.member.getVariant().getFullDesignationWithHullName()));
673 damage = 0.7f + (float) Math.random() * 0.1f;
674 break;
675 case MEDIUM_DAMAGE:
676 report(String.format("%40s: medium damage", data.member.getVariant().getFullDesignationWithHullName()));
677 damage = 0.45f + (float) Math.random() * 0.1f;
678 break;
679 case LIGHT_DAMAGE:
680 report(String.format("%40s: light damage", data.member.getVariant().getFullDesignationWithHullName()));
681 damage = 0.2f + (float) Math.random() * 0.1f;
682 break;
683 case UNSCATHED:
684 report(String.format("%40s: unscathed", data.member.getVariant().getFullDesignationWithHullName()));
685 damage = 0f;
686 break;
687 }
688
689 //damage = 0.8f;
690 applyDamageToFleetMember(data.member, damage);
691
692 return outcome;
693 }
694
695 protected FleetAutoresolveData computeDataForFleet(CampaignFleetAPI fleet) {
696 FleetAutoresolveData fleetData = new FleetAutoresolveData();
697 fleetData.fleet = fleet;
698
699 fleetData.fightingStrength = 0;
700 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
701 FleetMemberAutoresolveData data = computeDataForMember(member);
702 fleetData.members.add(data);
703 boolean okToDeployIfInPlayerPursuit = playerPursuitAutoresolveMode && playerShipsToDeploy != null && fleet == one && (playerShipsToDeploy.contains(member) || member.isAlly());
704 if (data.combatReady && (!playerPursuitAutoresolveMode || fleet != one || okToDeployIfInPlayerPursuit)) {
705 float mult = 1f;
706 if (playerShipsToDeploy != null && playerShipsToDeploy.contains(member)) {
707 mult = 8f;
708 }
709 fleetData.fightingStrength += data.strength * mult;
710 }
711 }
712
713 return fleetData;
714 }
715
716 protected FleetMemberAutoresolveData computeDataForMember(FleetMemberAPI member) {
717 FleetMemberAutoresolveData data = new FleetMemberAutoresolveData();
718
719 data.member = member;
720 ShipHullSpecAPI hullSpec = data.member.getHullSpec();
721 if ((member.isCivilian() && !playerPursuitAutoresolveMode) || !member.canBeDeployedForCombat()) {
722 data.strength = 0.25f;
723 if (hullSpec.getShieldType() != ShieldType.NONE) {
724 data.shieldRatio = 0.5f;
725 }
726 data.combatReady = false;
727 return data;
728 }
729
730 data.combatReady = true;
731
732// if (data.member.getHullId().contains("astral")) {
733// System.out.println("testtesttest");
734// }
735
736 MutableShipStatsAPI stats = data.member.getStats();
737
738 float normalizedHullStr = stats.getHullBonus().computeEffective(hullSpec.getHitpoints()) +
739 stats.getArmorBonus().computeEffective(hullSpec.getArmorRating()) * 10f;
740
741 float normalizedShieldStr = stats.getFluxCapacity().getModifiedValue() +
742 stats.getFluxDissipation().getModifiedValue() * 10f;
743
744
745 if (hullSpec.getShieldType() == ShieldType.NONE) {
746 normalizedShieldStr = 0;
747 } else {
748 float shieldFluxPerDamage = hullSpec.getBaseShieldFluxPerDamageAbsorbed();
749 shieldFluxPerDamage *= stats.getShieldAbsorptionMult().getModifiedValue() * stats.getShieldDamageTakenMult().getModifiedValue();;
750 if (shieldFluxPerDamage < 0.1f) shieldFluxPerDamage = 0.1f;
751 float shieldMult = 1f / shieldFluxPerDamage;
752 normalizedShieldStr *= shieldMult;
753 }
754
755 if (normalizedHullStr < 1) normalizedHullStr = 1;
756 if (normalizedShieldStr < 1) normalizedShieldStr = 1;
757
758 data.shieldRatio = normalizedShieldStr / (normalizedShieldStr + normalizedHullStr);
759 if (member.isStation()) {
760 data.shieldRatio = 0.5f;
761 }
762
763// float strength = member.getMemberStrength();
764//
765// strength *= 0.5f + 0.5f * member.getStatus().getHullFraction();
766// float captainMult = 0.5f;
767// if (member.getCaptain() != null) {
768// //captainMult = (10f + member.getCaptain().getStats().getAptitudeLevel("combat")) / 20f;
769// float captainLevel = member.getCaptain().getStats().getLevel();
770// captainMult += captainLevel / 20f;
771// }
772//
773// strength *= captainMult;
774
775 float strength = Misc.getMemberStrength(member, true, true, true);
776
777 strength *= 0.85f + 0.3f * (float) Math.random();
778
779 data.strength = Math.max(strength, 0.25f);
780
781 return data;
782 }
783
784
785 protected static void report(String str) {
786 if (report) {
787 System.out.println(str);
788 }
789 }
790
791 protected static boolean report = false;
792 protected EngagementResultAPI result;
794
795 public void setReport(boolean report) {
796 BattleAutoresolverPluginImpl.report = report;
797 }
798
799 public EngagementResultAPI getResult() {
800 return result;
801 }
802
803 public FleetEncounterContextPlugin getContext() {
804 return context;
805 }
806
807}
808
809
810
811
812
813
814
815
816
817
static SettingsAPI getSettings()
Definition Global.java:51
void resolveEngagement(FleetEncounterContext context, boolean oneEscaping, boolean twoEscaping)
static void applyDamageToFleetMember(FleetMemberAPI member, float hullFraction)
void resolvePlayerPursuit(FleetEncounterContext context, List< FleetMemberAPI > playerShipsToDeploy)
FleetMemberAutoresolveData computeDataForMember(FleetMemberAPI member)
FleetMemberBattleOutcome computeOutcomeForFleetMember(FleetMemberAutoresolveData data, float advantageInBattle, float maxDamage, boolean escaping, boolean enemyEscaping)
void generateLoot(List< FleetMemberAPI > recoveredShips, boolean withCredits)
DataForEncounterSide getDataFor(CampaignFleetAPI participantOrCombined)
boolean canOutrunOtherFleet(CampaignFleetAPI fleet, CampaignFleetAPI other)