1package com.fs.starfarer.api.impl.campaign;
3import java.util.ArrayList;
4import java.util.Collections;
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;
33 public EngagementResultForFleetImpl winnerResult, loserResult;
37 winnerResult =
new EngagementResultForFleetImpl(winner);
38 loserResult =
new EngagementResultForFleetImpl(loser);
45 public boolean didPlayerWin() {
58 public boolean isPlayerOutBeforeEnd() {
62 public void setPlayerOutBeforeEnd(
boolean playerOutBeforeEnd) {
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>();
92 public List<FleetMemberAPI> getDeployed() {
95 public List<FleetMemberAPI> getDestroyed() {
98 public List<FleetMemberAPI> getDisabled() {
107 public List<FleetMemberAPI> getReserves() {
110 public List<FleetMemberAPI> getRetreated() {
113 public List<DeployedFleetMemberAPI> getAllEverDeployedCopy() {
116 public boolean isWinner() {
119 public void setWinner(
boolean winner) {
120 this.winner = winner;
122 public void resetAllEverDeployed() {
127 public boolean isPlayer() {
131 public boolean enemyCanCleanDisengage() {
137 public static enum FleetMemberBattleOutcome {
145 public static class FleetMemberAutoresolveData {
147 public float strength;
148 public float shieldRatio;
149 public boolean combatReady;
152 public static class FleetAutoresolveData {
154 public float fightingStrength;
155 public List<FleetMemberAutoresolveData> members =
new ArrayList<FleetMemberAutoresolveData>();
157 public void report() {
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(),
208 if (optionOne == EncounterOption.DISENGAGE && optionTwo == EncounterOption.DISENGAGE) {
209 report(
"Both fleets want to disengage");
210 report(
"Finished autoresolving engagement");
216 boolean oneEscaping =
false;
217 boolean twoEscaping =
false;
219 boolean freeDisengageIfCanOutrun =
false;
221 if (optionOne == EncounterOption.DISENGAGE && optionTwo == EncounterOption.ENGAGE) {
226 report(
"Finished autoresolving engagement");
232 if (optionOne == EncounterOption.ENGAGE && optionTwo == EncounterOption.DISENGAGE) {
237 report(
"Finished autoresolving engagement");
247 report(
"Finished autoresolving engagement");
265 if (dataOne.fightingStrength <= 0 && dataTwo.fightingStrength <= 0) {
268 if (dataOne.fightingStrength <= 0.1f) {
269 dataOne.fightingStrength = 0.1f;
271 if (dataTwo.fightingStrength <= 0.1f) {
272 dataTwo.fightingStrength = 0.1f;
275 FleetAutoresolveData winner, loser;
278 report(
"--------------------------------------------");
282 report(
"--------------------------------------------");
288 boolean loserEscaping =
false;
289 if ((dataOne.fightingStrength > dataTwo.fightingStrength || twoEscaping) && !oneEscaping) {
294 loserEscaping =
true;
301 loserEscaping =
true;
305 float winnerAdvantage = winner.fightingStrength / loser.fightingStrength;
308 if (winnerAdvantage > 10f) winnerAdvantage = 10f;
309 if (winnerAdvantage < 0.1f) winnerAdvantage = 0.1f;
312 float damageDealtToWinner = loser.fightingStrength / winnerAdvantage;
313 float damageDealtToLoser = winner.fightingStrength * winnerAdvantage;
315 damageDealtToWinner = 0f;
319 damageDealtToWinner *= damMult;
320 damageDealtToLoser *= damMult;
328 report(
"Applying damage to loser's ships");
329 report(
"--------------------------------------------");
330 Collections.shuffle(loser.members);
332 for (FleetMemberAutoresolveData data : loser.members) {
333 report(String.format(
"Remaining damage to loser: %02.2f", damageDealtToLoser));
335 damageDealtToLoser -= data.strength;
336 if (damageDealtToLoser < 0) damageDealtToLoser = 0;
339 for (FleetMemberAutoresolveData data : loser.members) {
349 report(
"Applying damage to winner's ships");
350 report(
"--------------------------------------------");
351 Collections.shuffle(winner.members);
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));
358 damageDealtToWinner -= data.strength;
359 if (damageDealtToWinner < 0) damageDealtToWinner = 0;
363 winnerCarrierLeft =
true;
369 float deployedStrength = 0f;
370 float maxDeployedStrength = loser.fightingStrength * 2f;
371 for (FleetMemberAutoresolveData data : winner.members) {
373 deployedStrength += data.strength;
377 for (FleetMemberAutoresolveData data : winner.members) {
386 if (deployedStrength < maxDeployedStrength) {
388 deployedStrength += data.strength;
444 for (FleetMemberAutoresolveData data : loser.members) {
455 float hullFraction) {
457 if (hullFraction <= 0)
return;
460 boolean someActiveRemaining =
false;
463 String fleetName =
"Unknown";
464 String fleetLoc =
"unknown";
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",
478 "" + (
int)Math.round(num - 1f),
480 throw new RuntimeException(info);
483 for (
int i = 0; i < num; i++) {
491 float dam = Math.min(hullFraction, 0.9f);
492 float hits = Math.min(5f, dam / 0.1f);
493 if (hits < 1) hits = 1;
495 for (
int j = 0; j < hits; j++) {
504 float damageMult = 1f;
506 damageMult = (float) Math.random();
510 float damage = hullFraction * damageMult;
511 if (damage <= 0)
continue;
515 float hits = Math.min(5f, damage / 0.1f);
516 if (hits < 1) hits = 1;
517 for (
int j = 0; j < hits; j++) {
526 someActiveRemaining =
true;
531 float farthestDetached = 0;
532 for (
int i = 1; i < num; i++) {
541 if (dist > farthestDetached) {
542 farthestDetached = dist;
547 for (
int i = 1; i < num; i++) {
556 if (dist <= farthestDetached + 200f) {
566 if (!someActiveRemaining || hullFraction >= 1f) {
567 for (
int i = 0; i < num; i++) {
578 float maxDamage,
boolean escaping,
boolean enemyEscaping) {
581 float unscathed = 1f;
582 float lightDamage = 0f;
583 float mediumDamage = 0f;
584 float heavyDamage = 0f;
603 float maxDamageRatio = maxDamage / data.strength;
604 if (maxDamageRatio > 1) maxDamageRatio = 1;
605 if (maxDamageRatio <= 0) maxDamageRatio = 0;
607 if (maxDamageRatio >= 0.8f) {
612 }
else if (maxDamageRatio >= 0.6f) {
617 }
else if (maxDamageRatio >= 0.4f) {
622 }
else if (maxDamageRatio >= 0.2f) {
627 }
else if (maxDamageRatio > 0) {
642 mediumDamage *= 0.7f;
648 unscathed *= advantageInBattle;
649 lightDamage *= advantageInBattle;
651 float shieldRatio = data.shieldRatio;
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;
664 heavyDamage += disabled;
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);
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));
681 FleetMemberBattleOutcome outcome = picker.
pick();
696 damage = 0.7f + (float) Math.random() * 0.1f;
700 damage = 0.45f + (float) Math.random() * 0.1f;
704 damage = 0.2f + (float) Math.random() * 0.1f;
719 FleetAutoresolveData fleetData =
new FleetAutoresolveData();
720 fleetData.fleet = fleet;
722 fleetData.fightingStrength = 0;
725 fleetData.members.add(data);
732 fleetData.fightingStrength += data.strength * mult;
742 FleetMemberAutoresolveData data =
new FleetMemberAutoresolveData();
744 data.member = member;
747 data.strength = 0.25f;
749 data.shieldRatio = 0.5f;
751 data.combatReady =
false;
755 data.combatReady =
true;
771 normalizedShieldStr = 0;
775 if (shieldFluxPerDamage < 0.1f) shieldFluxPerDamage = 0.1f;
776 float shieldMult = 1f / shieldFluxPerDamage;
777 normalizedShieldStr *= shieldMult;
780 if (normalizedHullStr < 1) normalizedHullStr = 1;
781 if (normalizedShieldStr < 1) normalizedShieldStr = 1;
783 data.shieldRatio = normalizedShieldStr / (normalizedShieldStr + normalizedHullStr);
785 data.shieldRatio = 0.5f;
802 strength *= 0.85f + 0.3f * (float) Math.random();
804 data.strength = Math.max(strength, 0.25f);
810 protected static void report(String str) {
812 System.out.println(str);
821 BattleAutoresolverPluginImpl.report =
report;
static SettingsAPI getSettings()
static void modifyDataForFleet(FleetAutoresolveData data)
float computeEffective(float baseValue)
BattleAutoresolverPluginImpl(BattleAPI battle)
EngagementResultAPI getResult()
EngagementResultAPI result
void resolveEngagement(FleetEncounterContext context, boolean oneEscaping, boolean twoEscaping)
List< FleetMemberAPI > playerShipsToDeploy
static void applyDamageToFleetMember(FleetMemberAPI member, float hullFraction)
FleetEncounterContextPlugin getContext()
void setReport(boolean report)
void resolvePlayerPursuit(FleetEncounterContext context, List< FleetMemberAPI > playerShipsToDeploy)
FleetEncounterContext context
static void report(String str)
FleetMemberAutoresolveData computeDataForMember(FleetMemberAPI member)
boolean playerPursuitAutoresolveMode
FleetMemberBattleOutcome computeOutcomeForFleetMember(FleetMemberAutoresolveData data, float advantageInBattle, float maxDamage, boolean escaping, boolean enemyEscaping)
FleetAutoresolveData computeDataForFleet(CampaignFleetAPI fleet)
void generateLoot(List< FleetMemberAPI > recoveredShips, boolean withCredits)
void applyAfterBattleEffectsIfThereWasABattle()
void recoverCrew(CampaignFleetAPI fleet)
DataForEncounterSide getDataFor(CampaignFleetAPI participantOrCombined)
float performPostVictoryRecovery(EngagementResultAPI result)
void setAutoresolve(boolean isAutoresolve)
boolean canOutrunOtherFleet(CampaignFleetAPI fleet, CampaignFleetAPI other)
void setBattle(BattleAPI battle)
void processEngagementResults(EngagementResultAPI result)
static final String VASTBULK
static float getMemberStrength(FleetMemberAPI member)
static boolean isPlayerOrCombinedContainingPlayer(CampaignFleetAPI fleet)
static boolean isActiveModule(ShipVariantAPI variant)
float getFloat(String key)
CampaignFleetAPI getCombinedFor(CampaignFleetAPI participantOrCombined)
CampaignFleetAPI getNonPlayerCombined()
CampaignFleetAPI getCombinedTwo()
CampaignFleetAPI getPlayerCombined()
boolean isPlayerInvolved()
CampaignFleetAPI getCombinedOne()
LocationAPI getContainingLocation()
CampaignFleetAIAPI getAI()
String getNameWithFaction()
FleetDataAPI getFleetData()
List< FleetMemberAPI > getDeployed()
List< FleetMemberAPI > getDisabled()
List< FleetMemberAPI > getReserves()
List< FleetMemberAPI > getRetreated()
CampaignFleetAPI getFleet()
List< FleetMemberAPI > getMembersListCopy()
EncounterOption pickEncounterOption(FleetEncounterContextPlugin context, CampaignFleetAPI otherFleet)
EngagementResultForFleetAPI getLoserResult()
EngagementResultForFleetAPI getWinnerResult()
StatBonus getArmorBonus()
MutableStat getShieldAbsorptionMult()
MutableStat getFluxCapacity()
MutableStat getFluxDissipation()
MutableStat getShieldDamageTakenMult()
WeaponSlotAPI getWeaponSlotAPI(String slotId)
float getBaseShieldFluxPerDamageAbsorbed()
ShieldType getShieldType()
List< String > getModuleSlots()
ShipVariantAPI getModuleVariant(String slotId)
String getFullDesignationWithHullName()
boolean hasHullMod(String id)
ShipHullSpecAPI getHullSpec()
ShipVariantAPI getVariant()
boolean canBeDeployedForCombat()
FleetDataAPI getFleetData()
FleetMemberStatusAPI getStatus()
ShipHullSpecAPI getHullSpec()
MutableShipStatsAPI getStats()
void setDetached(int index, Boolean detached)
boolean isDetached(int index)
void applyHullFractionDamage(float fraction)
void setHullFraction(float fraction)