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