1package com.fs.starfarer.api.impl.campaign;
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.Iterator;
11import java.util.Random;
14import com.fs.starfarer.api.Global;
15import com.fs.starfarer.api.campaign.BattleAPI;
16import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
17import com.fs.starfarer.api.campaign.CampaignFleetAPI;
18import com.fs.starfarer.api.campaign.CargoAPI;
19import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
20import com.fs.starfarer.api.campaign.CargoStackAPI;
21import com.fs.starfarer.api.campaign.CombatDamageData;
22import com.fs.starfarer.api.campaign.CombatDamageData.DamageToFleetMember;
23import com.fs.starfarer.api.campaign.CombatDamageData.DealtByFleetMember;
24import com.fs.starfarer.api.campaign.EngagementResultForFleetAPI;
25import com.fs.starfarer.api.campaign.FactionAPI;
26import com.fs.starfarer.api.campaign.FleetAssignment;
27import com.fs.starfarer.api.campaign.FleetDataAPI;
28import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin;
29import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.DataForEncounterSide.OfficerEngagementData;
30import com.fs.starfarer.api.campaign.InteractionDialogAPI;
31import com.fs.starfarer.api.campaign.TextPanelAPI;
32import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.PursuitOption;
33import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
34import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
35import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
36import com.fs.starfarer.api.campaign.rules.MemoryAPI;
37import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
38import com.fs.starfarer.api.characters.OfficerDataAPI;
39import com.fs.starfarer.api.characters.PersonAPI;
40import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
41import com.fs.starfarer.api.combat.EngagementResultAPI;
42import com.fs.starfarer.api.combat.FighterLaunchBayAPI;
43import com.fs.starfarer.api.combat.ShipAPI;
44import com.fs.starfarer.api.combat.ShipVariantAPI;
45import com.fs.starfarer.api.combat.WeaponAPI;
46import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
47import com.fs.starfarer.api.fleet.CrewCompositionAPI;
48import com.fs.starfarer.api.fleet.FleetGoal;
49import com.fs.starfarer.api.fleet.FleetMemberAPI;
50import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
51import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
52import com.fs.starfarer.api.impl.campaign.ids.Commodities;
53import com.fs.starfarer.api.impl.campaign.ids.Factions;
54import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
55import com.fs.starfarer.api.impl.campaign.ids.Stats;
56import com.fs.starfarer.api.impl.campaign.ids.Tags;
57import com.fs.starfarer.api.impl.campaign.intel.PromoteOfficerIntel;
58import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData;
59import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageEntity;
60import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BaseSalvageSpecial;
61import com.fs.starfarer.api.impl.campaign.tutorial.TutorialMissionIntel;
62import com.fs.starfarer.api.loading.FighterWingSpecAPI;
63import com.fs.starfarer.api.loading.HullModSpecAPI;
64import com.fs.starfarer.api.loading.VariantSource;
65import com.fs.starfarer.api.loading.WeaponGroupSpec;
66import com.fs.starfarer.api.loading.WeaponSlotAPI;
67import com.fs.starfarer.api.loading.WeaponSpecAPI;
68import com.fs.starfarer.api.util.Misc;
69import com.fs.starfarer.api.util.WeightedRandomPicker;
73 protected List<DataForEncounterSide>
sideData =
new ArrayList<DataForEncounterSide>();
108 public DataForEncounterSide
getDataFor(CampaignFleetAPI participantOrCombined) {
109 CampaignFleetAPI combined =
battle.getCombinedFor(participantOrCombined);
110 if (combined ==
null) {
111 return new DataForEncounterSide(participantOrCombined);
114 for (DataForEncounterSide curr :
sideData) {
115 if (curr.getFleet() == combined)
return curr;
117 DataForEncounterSide dfes =
new DataForEncounterSide(combined);
124 for (DataForEncounterSide curr :
sideData) {
125 if (!curr.disengaged()) {
133 for (DataForEncounterSide curr :
sideData) {
134 if (curr.disengaged()) {
158 DataForEncounterSide data =
getDataFor(result.getFleet());
159 data.getMemberToDeployedMap().clear();
161 List<DeployedFleetMemberAPI> deployed = result.getAllEverDeployedCopy();
162 if (deployed !=
null && !deployed.isEmpty()) {
163 for (DeployedFleetMemberAPI dfm : deployed) {
164 if (dfm.getMember() !=
null) {
165 FleetMemberAPI member = dfm.getMember();
166 data.getMemberToDeployedMap().put(member, dfm);
168 if (dfm !=
null && dfm.getShip() !=
null && dfm.getShip().getOriginalCaptain() !=
null &&
169 !dfm.getShip().getOriginalCaptain().isDefault()) {
170 data.getMembersWithOfficerOrPlayerAsOrigCaptain().add(member);
183 Iterator<FleetMemberAPI> iter = result.getDeployed().iterator();
184 while (iter.hasNext()) {
185 FleetMemberAPI member = iter.next();
186 if (
battle.getSourceFleet(member) ==
null) {
190 iter = result.getReserves().iterator();
191 while (iter.hasNext()) {
192 FleetMemberAPI member = iter.next();
193 if (
battle.getSourceFleet(member) ==
null) {
197 iter = result.getDestroyed().iterator();
198 while (iter.hasNext()) {
199 FleetMemberAPI member = iter.next();
200 if (
battle.getSourceFleet(member) ==
null) {
204 iter = result.getDisabled().iterator();
205 while (iter.hasNext()) {
206 FleetMemberAPI member = iter.next();
207 if (
battle.getSourceFleet(member) ==
null) {
211 iter = result.getRetreated().iterator();
212 while (iter.hasNext()) {
213 FleetMemberAPI member = iter.next();
214 if (
battle.getSourceFleet(member) ==
null) {
234 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
235 EngagementResultForFleetAPI loserResult = result.getLoserResult();
241 CombatDamageData currDamageData = result.getLastCombatDamageData();
242 if (currDamageData !=
null) {
252 if (
battle.isPlayerInvolved()) {
263 if (
battle.isPlayerSide(winnerResult) && winnerResult.getGoal() != FleetGoal.ESCAPE) {
265 if (loserResult.getGoal() == FleetGoal.ESCAPE) {
269 }
else if (
battle.isPlayerSide(loserResult) && loserResult.getGoal() != FleetGoal.ESCAPE) {
271 if (winnerResult.getGoal() == FleetGoal.ESCAPE) {
276 DataForEncounterSide winnerData =
getDataFor(winnerResult.getFleet());
277 DataForEncounterSide loserData =
getDataFor(loserResult.getFleet());
279 winnerData.setWonLastEngagement(
true);
280 winnerData.setEnemyCanCleanDisengage(winnerResult.enemyCanCleanDisengage());
282 loserData.setWonLastEngagement(
false);
283 loserData.setEnemyCanCleanDisengage(loserResult.enemyCanCleanDisengage());
286 winnerData.setDidEnoughToDisengage(
true);
287 float damageInFP = 0f;
288 for (FleetMemberAPI member : winnerResult.getDisabled()) {
289 damageInFP += member.getFleetPointCost();
291 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
292 damageInFP += member.getFleetPointCost();
294 for (FleetMemberAPI member : winnerResult.getRetreated()) {
295 damageInFP += member.getFleetPointCost();
303 loserData.setDidEnoughToDisengage(winnerResult.enemyCanCleanDisengage());
306 winnerData.setLastGoal(winnerResult.getGoal());
307 loserData.setLastGoal(loserResult.getGoal());
309 winnerData.getDeployedInLastEngagement().clear();
310 winnerData.getRetreatedFromLastEngagement().clear();
311 winnerData.getInReserveDuringLastEngagement().clear();
312 winnerData.getDisabledInLastEngagement().clear();
313 winnerData.getDestroyedInLastEngagement().clear();
314 winnerData.getDeployedInLastEngagement().addAll(winnerResult.getDeployed());
315 winnerData.getRetreatedFromLastEngagement().addAll(winnerResult.getRetreated());
316 winnerData.getInReserveDuringLastEngagement().addAll(winnerResult.getReserves());
317 winnerData.getDisabledInLastEngagement().addAll(winnerResult.getDisabled());
318 winnerData.getDestroyedInLastEngagement().addAll(winnerResult.getDestroyed());
320 loserData.getDeployedInLastEngagement().clear();
321 loserData.getRetreatedFromLastEngagement().clear();
322 loserData.getInReserveDuringLastEngagement().clear();
323 loserData.getDisabledInLastEngagement().clear();
324 loserData.getDestroyedInLastEngagement().clear();
325 loserData.getDeployedInLastEngagement().addAll(loserResult.getDeployed());
326 loserData.getRetreatedFromLastEngagement().addAll(loserResult.getRetreated());
327 loserData.getInReserveDuringLastEngagement().addAll(loserResult.getReserves());
328 loserData.getDisabledInLastEngagement().addAll(loserResult.getDisabled());
329 loserData.getDestroyedInLastEngagement().addAll(loserResult.getDestroyed());
331 for (FleetMemberAPI member : loserResult.getDestroyed()) {
332 loserData.addOwn(member, Status.DESTROYED);
335 for (FleetMemberAPI member : loserResult.getDisabled()) {
336 loserData.addOwn(member, Status.DISABLED);
339 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
340 winnerData.addOwn(member, Status.DESTROYED);
343 for (FleetMemberAPI member : winnerResult.getDisabled()) {
344 winnerData.addOwn(member, Status.DISABLED);
348 if (result.getWinnerResult().getAllEverDeployedCopy() !=
null) {
352 if (result.getLoserResult().getAllEverDeployedCopy() !=
null) {
358 winnerResult.resetAllEverDeployed();
359 getDataFor(winnerResult.getFleet()).getMemberToDeployedMap().clear();
360 loserResult.resetAllEverDeployed();
361 getDataFor(loserResult.getFleet()).getMemberToDeployedMap().clear();
365 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
366 loserData.addEnemy(member, Status.DESTROYED);
368 for (FleetMemberAPI member : winnerResult.getDisabled()) {
369 loserData.addEnemy(member, Status.DISABLED);
372 for (FleetMemberAPI member : loserResult.getDestroyed()) {
373 winnerData.addEnemy(member, Status.DESTROYED);
375 for (FleetMemberAPI member : loserResult.getDisabled()) {
376 winnerData.addEnemy(member, Status.DISABLED);
380 FleetGoal winnerGoal = winnerResult.getGoal();
381 FleetGoal loserGoal = loserResult.getGoal();
382 boolean totalWin = loserData.getFleet().getFleetData().getMembersListCopy().isEmpty();
383 boolean playerOut = result.isPlayerOutBeforeEnd();
386 FleetGoal playerGoal =
null;
387 FleetGoal otherGoal =
null;
388 if (
battle.isPlayerSide(
battle.getSideFor(winnerResult.getFleet()))) {
389 playerGoal = winnerGoal;
390 otherGoal = loserGoal;
392 playerGoal = loserGoal;
393 otherGoal = winnerGoal;
395 if (playerGoal == FleetGoal.ATTACK) {
396 if (otherGoal == FleetGoal.ATTACK) {
397 if (winnerResult.isPlayer()) {
398 lastOutcome = EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN;
400 lastOutcome = EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS;
403 if (winnerResult.isPlayer()) {
404 lastOutcome = EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN;
406 lastOutcome = EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_LOSS;
410 if (winnerResult.isPlayer()) {
411 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN;
413 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_LOSS;
417 if (totalWin && winnerData.getFleet().getFleetData().getMembersListCopy().isEmpty()) {
418 lastOutcome = EngagementOutcome.MUTUAL_DESTRUCTION;
420 if (
battle.isPlayerSide(
battle.getSideFor(winnerResult.getFleet()))) {
421 if (winnerGoal == FleetGoal.ATTACK && loserGoal == FleetGoal.ATTACK) {
423 lastOutcome = EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL;
427 }
else if (winnerGoal == FleetGoal.ESCAPE) {
429 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
433 }
else if (loserGoal == FleetGoal.ESCAPE) {
435 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL;
437 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_SUCCESS;
441 if (winnerGoal == FleetGoal.ATTACK && loserGoal == FleetGoal.ATTACK) {
443 lastOutcome = EngagementOutcome.BATTLE_ENEMY_WIN_TOTAL;
447 }
else if (winnerGoal == FleetGoal.ESCAPE) {
449 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_WIN_TOTAL;
453 }
else if (loserGoal == FleetGoal.ESCAPE) {
455 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_LOSS_TOTAL;
457 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_SUCCESS;
469 protected void tallyOfficerTime(DataForEncounterSide data, EngagementResultForFleetAPI result) {
471 for (DeployedFleetMemberAPI dfm : result.getAllEverDeployedCopy()) {
472 float time = dfm.getShip().getFullTimeDeployed();
474 if (time > maxTime) {
478 time -= dfm.getShip().getTimeDeployedUnderPlayerControl();
479 if (time <= 0)
continue;
481 PersonAPI person = dfm.getMember().getCaptain();
482 CampaignFleetAPI source =
battle.getSourceFleet(dfm.getMember());
483 if (source ==
null)
continue;
486 if (source.getFleetData().getOfficerData(person) ==
null) {
487 OfficerEngagementData oed = data.getFleetMemberDeploymentData().get(dfm.getMember());
489 oed =
new OfficerEngagementData(source);
491 data.getFleetMemberDeploymentData().put(dfm.getMember(), oed);
493 time += dfm.getShip().getTimeDeployedUnderPlayerControl();
494 oed.timeDeployed += time;
498 OfficerEngagementData oed = data.getOfficerData().get(person);
500 oed =
new OfficerEngagementData(source);
502 data.getOfficerData().put(person, oed);
504 oed.timeDeployed += time;
506 data.setMaxTimeDeployed(data.getMaxTimeDeployed() + maxTime);
510 DataForEncounterSide otherData =
getDataFor(otherFleet);
512 if (otherData.isWonLastEngagement())
return PursueAvailability.LOST_LAST_ENGAGEMENT;
514 if (fleet.getFleetData().getCombatReadyMembersListCopy().isEmpty())
return PursueAvailability.NO_READY_SHIPS;
515 if (otherData.isDidEnoughToDisengage())
return PursueAvailability.TOOK_SERIOUS_LOSSES;
516 return PursueAvailability.AVAILABLE;
520 DataForEncounterSide otherData =
getDataFor(otherFleet);
521 if (otherData.isWonLastEngagement())
return DisengageHarryAvailability.LOST_LAST_ENGAGEMENT;
522 if (fleet.getFleetData().getCombatReadyMembersListCopy().isEmpty())
return DisengageHarryAvailability.NO_READY_SHIPS;
524 return DisengageHarryAvailability.AVAILABLE;
528 return member.getDeployCost();
534 getBattle().getPrimary(
getBattle().getNonPlayerSide()).getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_LOW_REP_IMPACT) ==
true;
540 getBattle().getPrimary(
getBattle().getNonPlayerSide()).getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NO_REP_IMPACT) ==
true;
548 public boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText,
boolean okToAdjustAlly,
boolean okToAdjustEnemy) {
554 boolean printedAdjustmentText =
false;
557 List<CampaignFleetAPI> playerSide =
battle.getPlayerSide();
558 List<CampaignFleetAPI> enemySide =
battle.getNonPlayerSide();
561 pf.setMoveDestination(pf.getLocation().x, pf.getLocation().y);
566 RepActions action =
null;
570 boolean knowsWhoPlayerIs =
battle.knowsWhoPlayerIs(enemySide);
574 for (CampaignFleetAPI enemy :
battle.getSnapshotFor(enemySide)) {
575 Misc.makeLowRepImpact(enemy,
"battleOnLowImpactSide");
579 if (knowsWhoPlayerIs && !lowImpact) {
580 action = RepActions.COMBAT_AGGRESSIVE;
582 action = RepActions.COMBAT_AGGRESSIVE_TOFF;
584 }
else if (playerWasAggressive) {
585 if (knowsWhoPlayerIs && !lowImpact) {
586 action = RepActions.COMBAT_NORMAL;
588 action = RepActions.COMBAT_NORMAL_TOFF;
596 if (!okToAdjustEnemy) action =
null;
598 Set<String> seen =
new HashSet<String>();
599 if (action !=
null) {
601 for (CampaignFleetAPI enemy :
battle.getSnapshotFor(enemySide)) {
602 String factionId = enemy.getFaction().getId();
603 if (seen.contains(factionId))
continue;
605 Global.
getSector().adjustPlayerReputation(
new RepActionEnvelope(action,
null, dialog.getTextPanel()), factionId);
606 printedAdjustmentText =
true;
611 action = RepActions.COMBAT_HELP_MINOR;
615 for (CampaignFleetAPI fleet :
battle.getSnapshotFor(playerSide)) {
616 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) {
617 if (fleet.isPlayerFleet()) {
618 playerFP += member.getFleetPointCost();
620 allyFP += member.getFleetPointCost();
624 for (CampaignFleetAPI fleet :
battle.getSnapshotFor(enemySide)) {
625 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) {
626 enemyFP += member.getFleetPointCost();
629 if (allyFP > enemyFP || !playerWon) {
630 action = RepActions.COMBAT_HELP_MINOR;
631 }
else if (allyFP < enemyFP * 0.5f) {
632 action = RepActions.COMBAT_HELP_CRITICAL;
634 action = RepActions.COMBAT_HELP_MAJOR;
645 }
else if (f < 0.1f) {
646 action = RepActions.COMBAT_HELP_MINOR;
649 if (action !=
null) {
652 action = RepActions.COMBAT_HELP_MINOR;
653 }
else if (totalDam < 20 && action == RepActions.COMBAT_HELP_CRITICAL) {
654 action = RepActions.COMBAT_HELP_MAJOR;
658 if (
battle.isPlayerInvolvedAtStart() && action !=
null) {
663 if (!okToAdjustAlly) action =
null;
670 for (CampaignFleetAPI ally :
battle.getSnapshotFor(playerSide)) {
671 if (ally.isPlayerFleet())
continue;
673 String factionId = ally.getFaction().getId();
674 if (seen.contains(factionId))
continue;
678 float threshold = 2f;
679 if (action == RepActions.COMBAT_HELP_MAJOR) {
681 }
else if (action == RepActions.COMBAT_HELP_CRITICAL) {
684 if (friendlyFPHull !=
null && friendlyFPHull > threshold) {
687 }
else if (action !=
null && playerSide.contains(ally)) {
689 Global.
getSector().adjustPlayerReputation(
new RepActionEnvelope(action,
null, dialog.getTextPanel()), factionId);
690 printedAdjustmentText =
true;
696 if (okToAdjustAlly) {
697 boolean first =
true;
699 for (CampaignFleetAPI ally :
battle.getSnapshotFor(playerSide)) {
700 if (ally.isPlayerFleet())
continue;
702 String factionId = ally.getFaction().getId();
703 if (Factions.PLAYER.equals(factionId))
continue;
704 if (seen.contains(factionId))
continue;
708 float threshold = 2f;
709 if (action == RepActions.COMBAT_HELP_MAJOR) {
711 }
else if (action == RepActions.COMBAT_HELP_CRITICAL) {
714 if (friendlyFPHull !=
null && friendlyFPHull > threshold) {
715 if (first && ffText !=
null) {
717 dialog.getTextPanel().addParagraph(ffText);
720 Global.
getSector().adjustPlayerReputation(
new RepActionEnvelope(RepActions.COMBAT_FRIENDLY_FIRE, (friendlyFPHull - threshold), dialog.getTextPanel()), factionId);
721 printedAdjustmentText =
true;
722 }
else if (action !=
null && playerSide.contains(ally)) {
729 return printedAdjustmentText;
755 for (FleetMemberAPI member :
Global.
getSector().getPlayerFleet().getFleetData().getMembersListCopy()) {
756 member.getStatus().resetAmmoState();
760 List<CampaignFleetAPI> otherSide =
battle.getNonPlayerSide();
761 CampaignFleetAPI fleet =
battle.getPrimary(otherSide);
762 if (fleet.getAI() !=
null &&
763 !fleet.getAI().isCurrentAssignment(FleetAssignment.STANDING_DOWN)) {
764 fleet.getAI().addAssignmentAtStart(FleetAssignment.STANDING_DOWN, fleet, 0.5f + 0.5f * (
float) Math.random(),
null);
782 if (winners ==
null || losers ==
null)
return;
784 for (CampaignFleetAPI loser : losers) {
785 for (FleetMemberAPI member : loser.getFleetData().getMembersListCopy()) {
786 member.getStatus().resetAmmoState();
789 loser.getVelocity().set(0, 0);
790 if (loser.isPlayerFleet())
continue;
791 if (loser.isPlayerFleet()) loser.setNoEngaging(3f);
794 for (CampaignFleetAPI winner : winners) {
795 for (FleetMemberAPI member : winner.getFleetData().getMembersListCopy()) {
796 member.getStatus().resetAmmoState();
799 winner.getVelocity().set(0, 0);
800 if (winner.isPlayerFleet())
continue;
801 if (winner.isPlayerFleet()) winner.setNoEngaging(3f);
805 if (
battle.isPlayerSide(winners)) {
806 for (CampaignFleetAPI fleet :
battle.getPlayerSide()) {
807 if (fleet.isPlayerFleet())
continue;
809 Misc.forgetAboutTransponder(fleet);
820 CampaignFleetAPI largestWinner =
battle.getPrimary(winners);
821 for (CampaignFleetAPI loser : losers) {
822 if (loser.getFleetData().getMembersListCopy().isEmpty()) {
824 loser.despawn(FleetDespawnReason.DESTROYED_BY_BATTLE,
battle);
828 for (CampaignFleetAPI winner : winners) {
829 if (winner.getFleetData().getMembersListCopy().isEmpty()) {
831 winner.despawn(FleetDespawnReason.DESTROYED_BY_BATTLE,
battle);
835 for (CampaignFleetAPI enemy :
battle.getBothSides()) {
836 if (enemy.getAI() instanceof ModularFleetAIAPI) {
837 ModularFleetAIAPI mAI = (ModularFleetAIAPI) enemy.getAI();
838 mAI.getTacticalModule().forceTargetReEval();
848 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
849 EngagementResultForFleetAPI loserResult = result.getLoserResult();
853 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
854 EngagementResultForFleetAPI loserResult = result.getLoserResult();
863 DataForEncounterSide winnerData =
getDataFor(winnerResult.getFleet());
864 DataForEncounterSide loserData =
getDataFor(loserResult.getFleet());
867 float loserDepDestroyed = 0f;
868 float loserDepLeft = 0f;
870 for (FleetMemberAPI member : loserData.getRetreatedFromLastEngagement()) {
871 loserDepLeft += member.getDeploymentPointsCost();
873 for (FleetMemberAPI member : loserData.getInReserveDuringLastEngagement()) {
874 loserDepLeft += member.getDeploymentPointsCost();
877 for (FleetMemberAPI member : loserData.getDestroyedInLastEngagement()) {
878 loserDepDestroyed += member.getDeploymentPointsCost();
880 for (FleetMemberAPI member : loserData.getDisabledInLastEngagement()) {
881 loserDepDestroyed += member.getDeploymentPointsCost();
883 for (FleetMemberAPI member : loserData.getRetreatedFromLastEngagement()) {
884 if (member.isFighterWing()) {
885 DeployedFleetMemberAPI dfm =
getDataFor(loserData.getFleet()).getMemberToDeployedMap().get(member);
886 if (dfm !=
null && dfm.getMember() == member) {
887 float deploymentCR = dfm.getShip().getWingCRAtDeployment();
888 float finalCR = deploymentCR;
890 if (deploymentCR > finalCR) {
891 float crPer = dfm.getMember().getStats().getCRPerDeploymentPercent().computeEffective(dfm.getMember().getVariant().getHullSpec().getCRToDeploy()) / 100f;
892 float extraCraftLost = (deploymentCR - finalCR) / crPer;
893 float wingSize = dfm.getMember().getNumFightersInWing();
894 if (extraCraftLost >= 1) {
895 loserDepDestroyed += Math.min(1f, extraCraftLost / wingSize) * member.getDeploymentPointsCost();
902 float totalRecovery = 0f;
904 for (FleetMemberAPI member : winnerData.getDeployedInLastEngagement()) {
905 float dp = member.getDeploymentPointsCost();
906 float recoveryFraction = Math.max(0, (dp * 1.25f - loserDepDestroyed)) / dp;
909 if (loserDepDestroyed > loserDepLeft * 2f) {
910 recoveryFraction = Math.max(0, (dp * 0.75f - loserDepDestroyed)) / dp;
912 if (recoveryFraction > 1f) recoveryFraction = 1f;
913 if (loserDepDestroyed <= 0) recoveryFraction = 1f;
918 if (prevCR < deployCost) {
923 float recoveryAmount = Math.round(deployCost * recoveryFraction * 100f) / 100f;
925 totalRecovery += recoveryAmount;
928 if (recoveryAmount <= 0)
continue;
931 member.getRepairTracker().applyCREvent(recoveryAmount,
"Post engagement recovery");
934 if (count <= 0)
return 0;
936 return Math.round(totalRecovery / count * 100f) / 100f;
941 public void applyPursuitOption(CampaignFleetAPI pursuingFleet, CampaignFleetAPI otherFleet, PursuitOption pursuitOption) {
943 if (Misc.isPlayerOrCombinedPlayerPrimary(pursuingFleet) && pursuitOption != PursuitOption.LET_THEM_GO) {
947 DataForEncounterSide pursuer =
getDataFor(pursuingFleet);
948 DataForEncounterSide other =
getDataFor(otherFleet);
950 if (pursuitOption == PursuitOption.HARRY) {
951 for (FleetMemberAPI member : otherFleet.getFleetData().getMembersListCopy()) {
954 float harryCost = deployCost * 1f;
955 member.getRepairTracker().applyCREvent(-harryCost,
"harried while disengaging");
971 lastOutcome != EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN &&
972 lastOutcome != EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS &&
973 lastOutcome != EngagementOutcome.BATTLE_ENEMY_WIN &&
974 lastOutcome != EngagementOutcome.BATTLE_PLAYER_WIN;
981 lastOutcome != EngagementOutcome.BATTLE_ENEMY_WIN &&
982 lastOutcome != EngagementOutcome.BATTLE_PLAYER_WIN;
986 return lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN ||
987 lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL ||
988 lastOutcome == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN ||
989 lastOutcome == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN ||
990 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN ||
991 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL ||
992 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_SUCCESS ||
993 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN ||
994 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
1022 return lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL ||
1024 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL ||
1025 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_SUCCESS ||
1026 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN ||
1027 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
1082 public static enum EngageBoardableOutcome {
1089 CampaignFleetAPI fleetItBelongsTo,
1090 CampaignFleetAPI attackingFleet) {
1091 float r = (float) Math.random();
1101 DataForEncounterSide attackerSide =
getDataFor(attackingFleet);
1102 attackerSide.changeEnemy(toBoard, Status.DISABLED);
1103 toBoard.getStatus().disable();
1104 return EngageBoardableOutcome.DISABLED;
1106 DataForEncounterSide attackerSide =
getDataFor(attackingFleet);
1107 attackerSide.changeEnemy(toBoard, Status.DESTROYED);
1108 toBoard.getStatus().disable();
1109 return EngageBoardableOutcome.DESTROYED;
1114 public static enum BoardingAttackType {
1116 LAUNCH_FROM_DISTANCE,
1118 public static enum BoardingOutcome {
1121 SUCCESS_TOO_DAMAGED,
1126 public static class BoardingResult {
1127 private BoardingOutcome outcome;
1130 private FleetMemberAPI member;
1131 private List<FleetMemberAPI> lostInSelfDestruct =
new ArrayList<FleetMemberAPI>();
1133 public BoardingOutcome getOutcome() {
1136 public List<FleetMemberAPI> getLostInSelfDestruct() {
1137 return lostInSelfDestruct;
1139 public void setOutcome(BoardingOutcome outcome) {
1140 this.outcome = outcome;
1142 public CrewCompositionAPI getAttackerLosses() {
1143 return attackerLosses;
1145 public void setAttackerLosses(CrewCompositionAPI attackerLosses) {
1146 this.attackerLosses = attackerLosses;
1148 public CrewCompositionAPI getDefenderLosses() {
1149 return defenderLosses;
1151 public void setDefenderLosses(CrewCompositionAPI defenderLosses) {
1152 this.defenderLosses = defenderLosses;
1154 public FleetMemberAPI getMember() {
1157 public void setMember(FleetMemberAPI member) {
1158 this.member = member;
1177 public BoardingResult
boardShip(FleetMemberAPI member, CampaignFleetAPI attacker, CampaignFleetAPI defender) {
1180 DataForEncounterSide attackerSide =
getDataFor(attacker);
1181 DataForEncounterSide defenderSide =
getDataFor(defender);
1183 float attackerMarineMult = attacker.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1184 float defenderMarineMult = defender.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1186 float crewMult = 2f;
1187 float marineMult = 7f;
1190 float attackerStr = attacker.getCargo().getMarines() * marineMult;
1191 attackerStr *= attackerMarineMult;
1193 CrewCompositionAPI defenderCrew = member.getCrewComposition();
1194 float defenderStr = defenderCrew.getCrew() * crewMult + defenderCrew.getMarines() * marineMult;
1195 defenderStr *= defenderMarineMult;
1199 Random rand =
new Random(1300000 * (member.getId().hashCode() + defender.getId().hashCode() +
Global.
getSector().getClock().getDay()));
1200 attackerStr *= 0.75f + 0.25f * rand.nextFloat();
1201 defenderStr *= 0.75f + 0.25f * rand.nextFloat();
1203 boolean attackerWin = attackerStr > defenderStr;
1204 boolean defenderWin = !attackerWin;
1206 BoardingResult result =
new BoardingResult();
1207 result.setMember(member);
1210 BoardingOutcome outcome = BoardingOutcome.SUCCESS;
1212 outcome = BoardingOutcome.SHIP_ESCAPED;
1216 boardingParty.addMarines(attacker.getCargo().getMarines());
1218 result.setOutcome(outcome);
1222 result.getAttackerLosses().removeFromCargo(attacker.getCargo());
1223 member.getCrewComposition().removeAll(result.getDefenderLosses());
1229 result.getAttackerLosses().removeFromCargo(attacker.getCargo());
1230 member.getCrewComposition().removeAll(result.getDefenderLosses());
1233 attacker.getFleetData().addFleetMember(member);
1234 getBattle().getCombinedFor(attacker).getFleetData().addFleetMember(member);
1236 member.getRepairTracker().setMothballed(
true);
1239 attackerSide.changeEnemy(member, Status.CAPTURED);
1240 defenderSide.changeOwn(member, Status.CAPTURED);
1242 attackerSide.getInReserveDuringLastEngagement().add(member);
1243 defenderSide.getDestroyedInLastEngagement().remove(member);
1244 defenderSide.getDisabledInLastEngagement().remove(member);
1255 DataForEncounterSide attackerSide =
getDataFor(attacker);
1256 DataForEncounterSide defenderSide =
getDataFor(defender);
1257 float attackerMarineMult = attacker.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1258 float defenderMarineMult = defender.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1260 float crewMult = 2f;
1261 float marineMult = 7f;
1263 Random rand =
new Random();
1268 for (
int i = 0; i < 100; i++) {
1269 float attackerStr = attacker.getCargo().getMarines() * marineMult;
1270 attackerStr *= attackerMarineMult;
1272 CrewCompositionAPI defenderCrew = member.getCrewComposition();
1273 float defenderStr = defenderCrew.getCrew() * crewMult + defenderCrew.getMarines() * marineMult;
1274 defenderStr *= defenderMarineMult;
1278 attackerStr *= 0.75f + 0.25f * rand.nextFloat();
1279 defenderStr *= 0.75f + 0.25f * rand.nextFloat();
1281 boolean attackerWin = attackerStr > defenderStr;
1282 if (attackerWin) wins++;
1291 result.getAttackerLosses().addAll(boardingParty);
1292 result.getAttackerLosses().multiplyBy((
float) Math.random() * 0.2f);
1296 FleetMemberAPI member, CrewCompositionAPI boardingParty,
1297 float attackerStr,
float defenderStr) {
1299 if (attackerStr < 1) attackerStr = 1;
1300 if (defenderStr < 1) defenderStr = 1;
1302 float attackerExtraStr = 0f;
1303 if (attackerStr > defenderStr * cap) {
1304 attackerExtraStr = attackerStr - defenderStr * cap;
1305 attackerStr = defenderStr * cap;
1307 if (defenderStr > attackerStr * cap) {
1308 defenderStr = attackerStr * cap;
1311 float attackerLosses = defenderStr / (attackerStr + defenderStr);
1312 float defenderLosses = attackerStr / (attackerStr + defenderStr);
1314 if (attackerStr > defenderStr) {
1315 result.getAttackerLosses().addAll(boardingParty);
1316 result.getAttackerLosses().multiplyBy(attackerLosses * attackerStr / (attackerExtraStr + attackerStr));
1317 result.getDefenderLosses().addAll(member.getCrewComposition());
1318 result.getDefenderLosses().multiplyBy(defenderLosses);
1320 result.getAttackerLosses().addAll(boardingParty);
1321 result.getAttackerLosses().multiplyBy(attackerLosses);
1322 result.getDefenderLosses().addAll(member.getCrewComposition());
1323 result.getDefenderLosses().multiplyBy(defenderLosses);
1330 CrewCompositionAPI boardingParty, BoardingAttackType attackType,
1331 List<FleetMemberAPI> boardingTaskForce,
1332 CampaignFleetAPI attacker, CampaignFleetAPI defender,
1333 BoardingResult result) {
1335 DataForEncounterSide attackerSide =
getDataFor(attacker);
1336 DataForEncounterSide defenderSide =
getDataFor(defender);
1338 attackerSide.changeEnemy(member, Status.DESTROYED);
1339 defenderSide.changeOwn(member, Status.DESTROYED);
1344 if (attackType == BoardingAttackType.SHIP_TO_SHIP) {
1345 for (FleetMemberAPI fm : boardingTaskForce) {
1346 float damage = member.getStats().getFluxCapacity().getModifiedValue() * (1f + (float) Math.random() * 0.5f);
1347 float hull = fm.getStatus().getHullFraction();
1348 float hullDamageFactor = 0f;
1349 fm.getStatus().applyDamage(damage);
1350 if (fm.getStatus().getHullFraction() <= 0) {
1351 fm.getStatus().disable();
1352 attacker.getFleetData().removeFleetMember(fm);
1353 attackerSide.addOwn(fm, Status.DESTROYED);
1356 attackerSide.getRetreatedFromLastEngagement().remove(fm);
1357 attackerSide.getInReserveDuringLastEngagement().remove(fm);
1358 attackerSide.getDeployedInLastEngagement().remove(fm);
1359 attackerSide.getDestroyedInLastEngagement().add(fm);
1361 result.getLostInSelfDestruct().add(fm);
1363 hullDamageFactor = 1f;
1365 float newHull = fm.getStatus().getHullFraction();
1366 float diff = hull - newHull;
1367 if (diff < 0) diff = 0;
1368 hullDamageFactor = diff;
1371 temp.addAll(fm.getCrewComposition());
1372 float lossFraction =
computeLossFraction(fm,
null, fm.getStatus().getHullFraction(), hullDamageFactor);
1373 temp.multiplyBy(lossFraction);
1380 total.setMarines(Math.round(Math.max(total.getMarines(), boardingParty.getMarines() * lossFraction)));
1381 total.setCrew(Math.round(Math.max(total.getCrew(), boardingParty.getCrew() * lossFraction)));
1387 result.getAttackerLosses().addAll(total);
1388 result.getDefenderLosses().addAll(member.getCrewComposition());
1391 public void letBoardableGo(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet) {
1392 DataForEncounterSide attackerSide =
getDataFor(attackingFleet);
1393 attackerSide.removeEnemyCasualty(toBoard);
1395 DataForEncounterSide defenderSide =
getDataFor(fleetItBelongsTo);
1396 defenderSide.removeOwnCasualty(toBoard);
1399 defenderSide.getDestroyedInLastEngagement().remove(toBoard);
1400 defenderSide.getDisabledInLastEngagement().remove(toBoard);
1401 defenderSide.getRetreatedFromLastEngagement().add(toBoard);
1403 if (!fleetItBelongsTo.isValidPlayerFleet()) {
1404 fleetItBelongsTo.getCargo().removeCrew(fleetItBelongsTo.getCargo().getCrew());
1405 fleetItBelongsTo.getCargo().removeMarines(fleetItBelongsTo.getCargo().getMarines());
1408 FleetDataAPI data = fleetItBelongsTo.getFleetData();
1409 data.addFleetMember(toBoard);
1411 getBattle().getCombinedFor(fleetItBelongsTo).getFleetData().addFleetMember(toBoard);
1413 toBoard.getCrewComposition().addToCargo(fleetItBelongsTo.getCargo());
1426 List<FleetMemberAPI> result =
new ArrayList<FleetMemberAPI>();
1433 if (Misc.isPlayerOrCombinedContainingPlayer(otherFleet)) {
1437 DataForEncounterSide winnerData =
getDataFor(winningFleet);
1438 DataForEncounterSide loserData =
getDataFor(otherFleet);
1441 List<FleetMemberData> enemyCasualties = winnerData.getEnemyCasualties();
1442 List<FleetMemberData> ownCasualties = winnerData.getOwnCasualties();
1443 List<FleetMemberData> all =
new ArrayList<FleetMemberData>();
1444 all.addAll(ownCasualties);
1445 Collections.sort(all,
new Comparator<FleetMemberData>() {
1446 public int compare(FleetMemberData o1, FleetMemberData o2) {
1447 int result = o2.getMember().getVariant().getSMods().size() - o1.getMember().getVariant().getSMods().size();
1449 result = o2.getMember().getHullSpec().getHullSize().ordinal() - o1.getMember().getHullSpec().getHullSize().ordinal();
1457 Random random = Misc.getRandom(
Global.
getSector().getPlayerBattleSeed(), 11);
1461 WeightedRandomPicker<FleetMemberData> enemyPicker =
new WeightedRandomPicker<FleetMemberData>(random);
1465 List<FleetMemberData> enemy =
new ArrayList<FleetMemberData>(enemyCasualties);
1466 Collections.sort(enemy,
new Comparator<FleetMemberData>() {
1467 public int compare(FleetMemberData o1, FleetMemberData o2) {
1468 int result = o2.getMember().getId().hashCode() - o1.getMember().getId().hashCode();
1473 for (FleetMemberData curr : enemy) {
1475 switch (curr.getMember().getHullSpec().getHullSize()) {
1476 case CAPITAL_SHIP: base = 40f;
break;
1477 case CRUISER: base = 20f;
break;
1478 case DESTROYER: base = 10f;
break;
1479 case FRIGATE: base = 5f;
break;
1481 float w = curr.getMember().getUnmodifiedDeploymentPointsCost() / base;
1483 enemyPicker.add(curr, w);
1485 List<FleetMemberData> sortedEnemy =
new ArrayList<FleetMemberData>();
1486 while (!enemyPicker.isEmpty()) {
1487 sortedEnemy.add(enemyPicker.pickAndRemove());
1491 all.addAll(sortedEnemy);
1499 int maxRecoverablePerType = 24;
1505 for (FleetMemberData data : all) {
1510 if (Misc.isUnboardable(data.getMember()))
continue;
1511 if (data.getStatus() != Status.DISABLED && data.getStatus() != Status.DESTROYED)
continue;
1513 boolean own = ownCasualties.contains(data);
1514 if (own && data.getMember().isAlly())
continue;
1521 if (data.getStatus() == Status.DESTROYED) mult = 0.5f;
1522 if (!own) mult *= playerContribMult;
1525 boolean useOfficerRecovery =
false;
1527 useOfficerRecovery = winnerData.getMembersWithOfficerOrPlayerAsOrigCaptain().contains(data.getMember());
1528 if (useOfficerRecovery) {
1533 boolean noRecovery =
false;
1535 battle.getSourceFleet(data.getMember()) !=
null) {
1536 CampaignFleetAPI fleet =
battle.getSourceFleet(data.getMember());
1537 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NO_SHIP_RECOVERY)) {
1545 boolean normalRecovery = !noRecovery &&
1546 Misc.isShipRecoverable(data.getMember(), playerFleet, own, useOfficerRecovery, 1f * mult);
1547 boolean storyRecovery = !noRecovery && !normalRecovery;
1549 boolean alwaysRec = data.getMember().getVariant().hasTag(Tags.VARIANT_ALWAYS_RECOVERABLE);
1551 float shipRecProb = data.getMember().getStats().getDynamic().getMod(Stats.INDIVIDUAL_SHIP_RECOVERY_MOD).computeEffective(0f);
1552 if (!own && !alwaysRec && (storyRecovery || normalRecovery) && shipRecProb < 1f) {
1558 float assumedAddedDmods = 3f;
1559 assumedAddedDmods -=
Global.
getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.SHIP_DMOD_REDUCTION, 0) * 0.5f;
1560 assumedAddedDmods = Math.min(assumedAddedDmods, 5 - dmods);
1562 float recoveredSoFar = 0f;
1564 else recoveredSoFar = result.size();
1566 if (random.nextFloat() < Math.min(max, (dmods + assumedAddedDmods) * per) + recoveredSoFar * perAlready) {
1573 if (!noRecovery && (normalRecovery || storyRecovery)) {
1576 if (!own || !Misc.isUnremovable(data.getMember().getCaptain())) {
1577 String aiCoreId =
null;
1578 if (own && data.getMember().getCaptain() !=
null &&
1579 data.getMember().getCaptain().isAICore()) {
1580 aiCoreId = data.getMember().getCaptain().getAICoreId();
1587 boolean keepCaptain =
false;
1596 if (aiCoreId !=
null) {
1597 data.getMember().getCaptain().getMemoryWithoutUpdate().set(
1598 "$aiCoreIdForRecovery", aiCoreId);
1603 ShipVariantAPI variant = data.getMember().getVariant();
1604 variant = variant.clone();
1605 variant.setSource(VariantSource.REFIT);
1606 variant.setOriginalVariant(
null);
1608 data.getMember().setVariant(variant,
false,
true);
1610 boolean lessDmods =
false;
1611 if (!own && data.getStatus() != Status.DESTROYED && random.nextFloat() < probLessDModsOnNext) {
1613 probLessDModsOnNext *= lessDmodsOnNextMult;
1617 Random dModRandom =
new Random(1000000 * data.getMember().getId().hashCode() +
Global.
getSector().getPlayerBattleSeed());
1618 dModRandom = Misc.getRandom(dModRandom.nextLong(), 5);
1620 DModManager.reduceNextDmodsBy = 3;
1623 float probAvoidDmods =
1624 data.getMember().getStats().getDynamic().getMod(
1625 Stats.DMOD_AVOID_PROB_MOD).computeEffective(0f);
1627 float probAcquireDmods =
1628 data.getMember().getStats().getDynamic().getMod(
1629 Stats.DMOD_ACQUIRE_PROB_MOD).computeEffective(1f);
1631 if (dModRandom.nextFloat() >= probAvoidDmods && dModRandom.nextFloat() < probAcquireDmods) {
1643 weaponProb = playerFleet.getStats().getDynamic().getValue(Stats.OWN_WEAPON_RECOVERY_MOD, weaponProb);
1644 wingProb = playerFleet.getStats().getDynamic().getValue(Stats.OWN_WING_RECOVERY_MOD, wingProb);
1647 boolean retain = data.getMember().getHullSpec().hasTag(Tags.TAG_RETAIN_SMODS_ON_RECOVERY) ||
1648 data.getMember().getVariant().hasTag(Tags.TAG_RETAIN_SMODS_ON_RECOVERY);
1649 prepareShipForRecovery(data.getMember(), own,
true, !own && !retain, weaponProb, wingProb, salvageRandom);
1651 if (normalRecovery) {
1652 if (result.size() < maxRecoverablePerType) {
1653 result.add(data.getMember());
1655 }
else if (storyRecovery) {
1684 if (!Misc.isPlayerOrCombinedContainingPlayer(winningFleet)) {
1688 DataForEncounterSide winnerData =
null;
1689 DataForEncounterSide loserData =
null;
1691 if (context !=
null) {
1692 winnerData = context.
getDataFor(winningFleet);
1698 for (FleetMemberAPI member : ships) {
1702 if (member.getStatus().getNumStatuses() <= 1) {
1703 member.getStatus().repairDisabledABit();
1712 float minHull = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MIN, 0f);
1713 float maxHull = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MAX, 0f);
1714 float minCR = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MIN, 0f);
1715 float maxCR = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MAX, 0f);
1717 minHull += member.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MIN, 0f);
1718 maxHull += member.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MAX, 0f);
1719 minCR += member.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MIN, 0f);
1720 maxCR += member.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MAX, 0f);
1722 float hull = (float) Math.random() * (maxHull - minHull) + minHull;
1723 if (hull < 0.01f) hull = 0.01f;
1724 if (hull > 1f) hull = 1f;
1725 member.getStatus().setHullFraction(hull);
1727 float cr = (float) Math.random() * (maxCR - minCR) + minCR;
1728 if (cr < 0 || member.isMothballed()) cr = 0;
1729 float max = member.getRepairTracker() ==
null ? 1f : member.getRepairTracker().getMaxCR();
1730 if (cr > max) cr = max;
1731 member.getRepairTracker().setCR(cr);
1733 if (winnerData !=
null) winnerData.getInReserveDuringLastEngagement().add(member);
1734 playerFleet.getFleetData().addFleetMember(member);
1735 if (context !=
null) {
1736 context.
getBattle().getCombinedFor(playerFleet).getFleetData().addFleetMember(member);
1737 context.
getBattle().getMemberSourceMap().put(member, playerFleet);
1740 member.setFleetCommanderForStats(
null,
null);
1745 if (!Misc.isUnremovable(member.getCaptain())) {
1747 member.getCaptain().setFaction(Factions.PLAYER);
1752 if (winnerData !=
null) {
1753 winnerData.changeEnemy(member, Status.REPAIRED);
1754 winnerData.changeOwn(member, Status.REPAIRED);
1756 winnerData.getDestroyedInLastEngagement().remove(member);
1757 winnerData.getDisabledInLastEngagement().remove(member);
1760 if (loserData !=
null) {
1761 loserData.changeEnemy(member, Status.REPAIRED);
1762 loserData.changeOwn(member, Status.REPAIRED);
1764 loserData.getDestroyedInLastEngagement().remove(member);
1765 loserData.getDisabledInLastEngagement().remove(member);
1774 boolean retainAllHullmods,
boolean retainKnownHullmods,
boolean clearSMods,
1775 float weaponRetainProb,
float wingRetainProb, Random salvageRandom) {
1776 ShipVariantAPI variant = member.getVariant().clone();
1777 variant.setOriginalVariant(
null);
1778 if (retainAllHullmods) {
1780 }
else if (retainKnownHullmods) {
1781 for (String modId :
new ArrayList<String>(variant.getHullMods())) {
1783 variant.removeMod(modId);
1787 variant.clearHullMods();
1788 variant.setNumFluxCapacitors(0);
1789 variant.setNumFluxVents(0);
1792 if (clearSMods && !variant.hasTag(Tags.VARIANT_ALWAYS_RETAIN_SMODS_ON_SALVAGE)) {
1793 for (String
id :
new ArrayList<String>(variant.getSMods())) {
1794 variant.removePermaMod(
id);
1798 variant.setSource(VariantSource.REFIT);
1799 member.setVariant(variant,
false,
false);
1800 List<String>
remove =
new ArrayList<String>();
1808 Random random =
new Random();
1809 if (salvageRandom !=
null) random = salvageRandom;
1811 if (!member.isFighterWing()) {
1812 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
1813 if (random.nextFloat() > weaponRetainProb) {
1817 for (String slotId :
remove) {
1818 variant.clearSlot(slotId);
1822 for (String
id : variant.getFittedWings()) {
1823 if (random.nextFloat() > wingRetainProb) {
1824 variant.setWingId(index,
null);
1830 for (String slotId : variant.getStationModules().keySet()) {
1832 retainAllHullmods, retainKnownHullmods, clearSMods, weaponRetainProb, wingRetainProb, salvageRandom);
1836 for (
int i = 1; i < member.getStatus().getNumStatuses(); i++) {
1837 if (random.nextFloat() > 0.5f) {
1838 member.getStatus().setDetached(i,
false);
1839 member.getStatus().setHullFraction(i, 0.1f + 0.1f * random.nextFloat());
1844 for (
int i = 0; i < 10; i++) {
1845 member.getBuffManager().advance(1f);
1851 variant.addTag(Tags.SHIP_RECOVERABLE);
1855 boolean retainAllHullmods,
boolean retainKnownHullmods,
boolean clearSMods,
1856 float weaponRetainProb,
float wingRetainProb, Random salvageRandom) {
1858 ShipVariantAPI moduleCurrent = member.getVariant().getModuleVariant(moduleSlotId);
1859 if (moduleCurrent ==
null)
return;
1861 moduleCurrent = moduleCurrent.clone();
1862 moduleCurrent.setOriginalVariant(
null);
1863 if (retainAllHullmods) {
1865 }
else if (retainKnownHullmods) {
1866 for (String modId :
new ArrayList<String>(moduleCurrent.getHullMods())) {
1868 moduleCurrent.removeMod(modId);
1872 moduleCurrent.clearHullMods();
1873 moduleCurrent.setNumFluxCapacitors(0);
1874 moduleCurrent.setNumFluxVents(0);
1877 if (clearSMods && !moduleCurrent.hasTag(Tags.VARIANT_ALWAYS_RETAIN_SMODS_ON_SALVAGE)) {
1878 for (String
id :
new ArrayList<String>(moduleCurrent.getSMods())) {
1879 moduleCurrent.removePermaMod(
id);
1883 moduleCurrent.setSource(VariantSource.REFIT);
1884 member.getVariant().setModuleVariant(moduleSlotId, moduleCurrent);
1886 List<String>
remove =
new ArrayList<String>();
1888 Random random = Misc.random;
1889 if (salvageRandom !=
null) random = salvageRandom;
1891 for (String slotId : moduleCurrent.getNonBuiltInWeaponSlots()) {
1892 if (random.nextFloat() > weaponRetainProb) {
1896 for (String slotId :
remove) {
1897 moduleCurrent.clearSlot(slotId);
1901 for (String
id : moduleCurrent.getFittedWings()) {
1902 if (random.nextFloat() > wingRetainProb) {
1903 moduleCurrent.setWingId(index,
null);
1912 if (!
battle.isPlayerInvolved())
return;
1914 DataForEncounterSide sideOne =
sideData.get(0);
1915 DataForEncounterSide sideTwo =
sideData.get(1);
1916 if (
battle.isPlayerSide(
battle.getSideFor(sideOne.getFleet()))) {
1917 gainXP(sideOne, sideTwo);
1918 }
else if (
battle.isPlayerSide(
battle.getSideFor(sideTwo.getFleet()))) {
1919 gainXP(sideTwo, sideOne);
1924 float max = data.getMaxTimeDeployed();
1925 if (max < 1) max = 1;
1926 float num = data.getOfficerData().size();
1927 if (num < 1) num = 1;
1928 for (PersonAPI person : data.getOfficerData().keySet()) {
1929 OfficerEngagementData oed = data.getOfficerData().get(person);
1930 if (oed.sourceFleet ==
null || !oed.sourceFleet.isPlayerFleet())
continue;
1932 OfficerDataAPI od = oed.sourceFleet.getFleetData().getOfficerData(person);
1933 if (od ==
null)
continue;
1935 float f = oed.timeDeployed / max;
1972 if (member.getOwner() != 0)
continue;
1976 for (FleetMemberAPI target : dealt.getDamage().keySet()) {
1977 if (
battle.getSourceFleet(target) ==
null)
continue;
1979 DamageToFleetMember damage = dealt.getDamageTo(target);
1980 float maxHull = target.getStats().getHullBonus().computeEffective(target.getHullSpec().getHitpoints());
1981 if (maxHull <= 0)
continue;
1982 if (target.isFighterWing()) {
1983 maxHull *= target.getNumFightersInWing();
1986 float currDam = Math.min(damage.hullDamage, maxHull) / maxHull * (float) target.getFleetPointCost();
1987 if (target.getOwner() == 1) {
1988 CampaignFleetAPI fleet =
battle !=
null ?
battle.getSourceFleet(member) :
null;
1989 boolean ally = member.isAlly();
1990 if (ally && fleet !=
null &&
1991 fleet.getFaction() !=
null && fleet.getFaction().isPlayerFaction()) {
1999 }
else if (!member.isAlly() && target.isAlly() && !target.isFighterWing()) {
2001 CampaignFleetAPI fleet =
battle !=
null ?
battle.getSourceFleet(target) :
null;
2002 if (fleet !=
null) {
2003 float curr = currDam;
2025 if (
battle ==
null)
return 1f;
2026 if (
battle.isPlayerInvolved() && (
battle.getPlayerSideSnapshot().size() <= 1 ||
battle.getPlayerSide().size() <= 1))
return 1f;
2030 boolean hasAllies =
false;
2031 boolean startedWithAllies =
false;
2033 hasAllies =
battle.getPlayerSide().size() <= 1;
2034 startedWithAllies =
battle.getPlayerSideSnapshot().size() > 1;
2036 if (startedWithAllies) {
2045 protected void gainXP(DataForEncounterSide side, DataForEncounterSide otherSide) {
2048 for (FleetMemberData data : side.getOwnCasualties()) {
2049 if (data.getStatus() == Status.DISABLED ||
2050 data.getStatus() == Status.DESTROYED) {
2051 float [] bonus = Misc.getBonusXPForScuttling(data.getMember());
2053 bonusXP += bonus[1] * bonus[0];
2056 if (bonusXP > 0 && points > 0) {
2058 Global.
getSector().getPlayerStats().setOnlyAddBonusXPDoNotSpendStoryPoints(
true);
2059 Global.
getSector().getPlayerStats().setBonusXPGainReason(
"from losing s-modded ships");
2061 Global.
getSector().getPlayerStats().setOnlyAddBonusXPDoNotSpendStoryPoints(
false);
2068 for (FleetMemberData data : otherSide.getOwnCasualties()) {
2069 float fp = data.getMember().getFleetPointCost();
2070 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2074 float xp = (float) fpTotal * 250;
2077 float difficultyMult = Math.max(1f,
difficulty);
2078 xp *= difficultyMult;
2104 if (!
battle.isPlayerInvolved())
return;
2106 DataForEncounterSide sideOne =
sideData.get(0);
2107 DataForEncounterSide sideTwo =
sideData.get(1);
2109 DataForEncounterSide player = sideOne;
2110 DataForEncounterSide enemy = sideTwo;
2111 if (
battle.isPlayerSide(
battle.getSideFor(sideTwo.getFleet()))) {
2116 float fpDestroyed = 0;
2117 for (FleetMemberData data : enemy.getOwnCasualties()) {
2118 float fp = data.getMember().getFleetPointCost();
2119 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2123 for (FleetMemberData data : player.getOwnCasualties()) {
2124 if (data.getMember().isAlly())
continue;
2125 float fp = data.getMember().getFleetPointCost();
2126 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2130 float fpInFleet = 0f;
2131 for (FleetMemberAPI member :
Global.
getSector().getPlayerFleet().getFleetData().getMembersListCopy()) {
2132 float fp = member.getFleetPointCost();
2133 fp *= 1f + member.getCaptain().getStats().getLevel() / 5f;
2141 int curr = Misc.getNumNonMercOfficers(
Global.
getSector().getPlayerFleet());
2143 float prob = fpDestroyed / (Math.max(1f, fpInFleet));
2147 if (curr >= max) prob *= 0.5f;
2148 if (prob > maxProb) prob = maxProb;
2150 Random random = Misc.random;
2151 if (salvageRandom !=
null) {
2152 random = salvageRandom;
2155 if (TutorialMissionIntel.isTutorialInProgress()) {
2159 if (random.nextFloat() < prob) {
2168 public void generateLoot(List<FleetMemberAPI> recoveredShips,
boolean withCredits) {
2180 private Random salvageRandom =
null;
2182 return salvageRandom;
2185 this.salvageRandom = salvageRandom;
2195 if (winner ==
null || loser ==
null)
return;
2197 float adjustedFPSalvage = 0;
2200 Random origSalvageRandom = salvageRandom;
2201 long extraSeed = 1340234324325L;
2202 if (origSalvageRandom !=
null) extraSeed = origSalvageRandom.nextLong();
2204 for (FleetMemberData data : winner.getEnemyCasualties()) {
2205 if (data.getStatus() == Status.REPAIRED) {
2209 if (origSalvageRandom !=
null) {
2210 String sig = data.getMember().getHullId();
2211 if (data.getMember().getVariant() !=
null) {
2212 for (WeaponGroupSpec spec : data.getMember().getVariant().getWeaponGroups()) {
2213 for (String slotId : spec.getSlots()) {
2214 String w = data.getMember().getVariant().getWeaponId(slotId);
2215 if (w !=
null) sig += w;
2219 if (loser !=
null && loser.getFleet() !=
null && loser.getFleet().getFleetData() !=
null) {
2220 List<FleetMemberAPI> members = loser.getFleet().getFleetData().getMembersListCopy();
2221 if (members !=
null) {
2222 int index = members.indexOf(data.getMember());
2228 long seed = sig.hashCode() * 143234234234L * extraSeed;
2229 salvageRandom =
new Random(seed);
2233 float mult =
getSalvageMult(data.getStatus()) * playerContribMult;
2234 lootWeapons(data.getMember(), data.getMember().getVariant(),
false, mult,
false);
2235 lootHullMods(data.getMember(), data.getMember().getVariant(), mult);
2236 lootWings(data.getMember(), data.getMember().getVariant(),
false, mult);
2237 adjustedFPSalvage += (float) data.getMember().getFleetPointCost() * mult;
2240 for (FleetMemberData data : winner.getOwnCasualties()) {
2241 if (data.getMember().isAlly())
continue;
2243 if (data.getStatus() == Status.CAPTURED || data.getStatus() == Status.REPAIRED) {
2253 lootWeapons(data.getMember(), data.getMember().getVariant(),
true, mult,
false);
2254 lootWings(data.getMember(), data.getMember().getVariant(),
true, mult);
2256 adjustedFPSalvage += (float) data.getMember().getFleetPointCost() * mult;
2259 if (recoveredShips !=
null) {
2260 for (FleetMemberAPI member : recoveredShips) {
2262 adjustedFPSalvage += (float) member.getFleetPointCost() * mult;
2266 salvageRandom = origSalvageRandom;
2269 Random resetSalvageRandomTo =
null;
2270 Random forRandomDrops =
null;
2271 Random forCargoDrops =
null;
2273 Random random = Misc.random;
2274 if (salvageRandom !=
null) {
2275 random = salvageRandom;
2276 resetSalvageRandomTo = Misc.getRandom(random.nextLong(), 11);
2277 forRandomDrops = Misc.getRandom(random.nextLong(), 17);
2278 forCargoDrops = Misc.getRandom(random.nextLong(), 31);
2281 MemoryAPI memory =
getBattle().getNonPlayerCombined().getMemoryWithoutUpdate();
2282 if (memory.contains(MemFlags.SALVAGE_SEED)) {
2283 random =
new Random(memory.getLong(MemFlags.SALVAGE_SEED));
2291 float creditsFraction = minCreditsFraction + (maxCreditsFraction - minCreditsFraction) * random.nextFloat();
2292 creditsFraction *= playerContribMult;
2295 if (Misc.isEasy()) {
2300 float valueMultFleet = playerFleet.getStats().getDynamic().getValue(Stats.BATTLE_SALVAGE_MULT_FLEET);
2304 maxSalvageValue *= valueMultFleet + valueModShips;
2306 creditsLooted = Math.round(maxSalvageValue * creditsFraction);
2310 float salvageValue = 0f;
2311 WeightedRandomPicker<String> lootPicker =
new WeightedRandomPicker<String>(random);
2312 lootPicker.add(Commodities.METALS, 20);
2313 lootPicker.add(Commodities.SUPPLIES, 10);
2314 lootPicker.add(Commodities.FUEL, 10);
2315 lootPicker.add(Commodities.HEAVY_MACHINERY, 1);
2317 while (salvageValue < maxSalvageValue) {
2318 String commodityId = lootPicker.pick();
2319 if (commodityId ==
null)
break;
2321 CommoditySpecAPI spec =
Global.
getSector().getEconomy().getCommoditySpec(commodityId);
2323 salvageValue += spec.getBasePrice() * qty;
2324 loot.addCommodity(commodityId, qty);
2328 float fuelMult = playerFleet.getStats().getDynamic().getValue(Stats.FUEL_SALVAGE_VALUE_MULT_FLEET);
2329 float fuel =
loot.getFuel();
2330 if (fuelMult > 1f) {
2331 loot.addFuel((
int) Math.round(fuel * (fuelMult - 1f)));
2334 if (
getBattle().getSnapshotSideFor(loser.getFleet()) ==
null)
return;
2336 List<DropData> dropRandom =
new ArrayList<DropData>();
2337 List<DropData> dropValue =
new ArrayList<DropData>();
2339 for (CampaignFleetAPI other :
getBattle().getSnapshotSideFor(loser.getFleet())) {
2340 dropRandom.addAll(other.getDropRandom());
2341 dropValue.addAll(other.getDropValue());
2342 other.getDropRandom().clear();
2343 other.getDropValue().clear();
2345 CargoAPI extra = BaseSalvageSpecial.getCombinedExtraSalvage(other);
2348 BaseSalvageSpecial.clearExtraSalvage(other);
2349 if (!extra.isEmpty()) {
2350 ListenerUtil.reportExtraSalvageShown(other);
2354 if (forRandomDrops !=
null) {
2355 random = forRandomDrops;
2357 CargoAPI extra = SalvageEntity.generateSalvage(random, valueMultFleet + valueModShips, 1f, fuelMult, dropValue, dropRandom);
2358 for (CargoStackAPI stack : extra.getStacksCopy()) {
2359 loot.addFromStack(stack);
2362 if (forCargoDrops !=
null) {
2363 salvageRandom = forCargoDrops;
2367 if (resetSalvageRandomTo !=
null) {
2368 salvageRandom = resetSalvageRandomTo;
2390 protected static class LootableCargoStack {
2391 public CargoAPI source;
2392 public CargoStackAPI stack;
2393 public LootableCargoStack(CargoAPI source, CargoStackAPI stack) {
2394 this.source = source;
2399 protected static class LossFraction {
2400 public float maxCargo;
2401 public float maxFuel;
2402 public float lostCargo;
2403 public float lostFuel;
2411 if (winner ==
null || loser ==
null)
return;
2413 loser.getFleet().getFleetData().updateCargoCapacities();
2414 CargoAPI loserCargo = (CargoAPI) loser.getFleet().getCargo();
2415 float maxCargo = loserCargo.getMaxCapacity();
2416 float maxFuel = loserCargo.getMaxFuel();
2418 Random random = Misc.random;
2419 if (salvageRandom !=
null) random = salvageRandom;
2423 Map<CargoAPI, LossFraction> fractions =
new HashMap<CargoAPI, LossFraction>();
2425 float lostCargo = 0f;
2426 float lostFuel = 0f;
2428 float totalLoss = 0f;
2429 for (FleetMemberData data : winner.getEnemyCasualties()) {
2437 CampaignFleetAPI source =
battle.getSourceFleet(data.getMember());
2438 if (source !=
null) {
2439 CargoAPI c = source.getCargo();
2440 LossFraction loss = fractions.get(c);
2442 loss =
new LossFraction();
2443 loss.maxCargo = c.getMaxCapacity();
2444 loss.maxFuel = c.getMaxFuel();
2445 fractions.put(c, loss);
2448 loss.lostCargo += data.getMember().getCargoCapacity();
2449 loss.lostFuel += data.getMember().getFuelCapacity();
2451 loss.maxCargo += data.getMember().getCargoCapacity();
2452 loss.maxFuel += data.getMember().getFuelCapacity();
2454 totalLoss += loss.maxCargo + loss.maxFuel;
2456 lostCargo += data.getMember().getCargoCapacity();
2457 lostFuel += data.getMember().getFuelCapacity();
2459 maxCargo += data.getMember().getCargoCapacity();
2460 maxFuel += data.getMember().getFuelCapacity();
2462 totalLoss += maxCargo + maxFuel;
2468 if (totalLoss <= 0) {
2472 if (maxCargo < 1) maxCargo = 1;
2473 if (maxFuel < 1) maxFuel = 1;
2477 if (
battle.isPlayerSide(
battle.getSideFor(winner.getFleet()))) {
2479 recoveryFraction *= playerContribMult;
2482 float cargoFractionLost = lostCargo / maxCargo;
2483 float fuelFractionLost = lostFuel / maxFuel;
2484 if (lostCargo > maxCargo) cargoFractionLost = 1f;
2485 if (lostFuel > maxFuel) fuelFractionLost = 1f;
2488 List<CampaignFleetAPI> losers =
battle.getSnapshotSideFor(loser.getFleet());
2489 if (losers ==
null)
return;
2491 List<LootableCargoStack> stacks =
new ArrayList<LootableCargoStack>();
2492 for (CampaignFleetAPI curr : losers) {
2493 for (CargoStackAPI stack : curr.getCargo().getStacksCopy()) {
2494 stacks.add(
new LootableCargoStack(curr.getCargo(), stack));
2498 for (LootableCargoStack stack : stacks) {
2499 if (stack.stack.isNull())
continue;
2500 if (stack.stack.isPersonnelStack())
continue;
2501 if (stack.stack.getSize() < 1)
continue;
2503 float actualCargoFractionLost = cargoFractionLost;
2504 float actualFuelFractionLost = fuelFractionLost;
2505 LossFraction loss = fractions.get(stack.source);
2507 actualCargoFractionLost = loss.lostCargo / loss.maxCargo;
2508 actualFuelFractionLost = loss.lostFuel / loss.maxFuel;
2509 if (loss.lostCargo > loss.maxCargo) actualCargoFractionLost = 1f;
2510 if (loss.lostFuel > loss.maxFuel) actualFuelFractionLost = 1f;
2514 if (takingFromPlayer) {
2515 if (stack.stack.isSpecialStack())
continue;
2516 if (stack.stack.isCommodityStack()) {
2517 CommoditySpecAPI spec = stack.stack.getResourceIfResource();
2518 if (spec !=
null && spec.hasTag(Commodities.TAG_NO_LOSS_FROM_COMBAT)){
2526 if (stack.stack.isFuelStack()) {
2527 numLost = actualFuelFractionLost * stack.stack.getSize();
2528 numTaken = Math.round(numLost * (0.5f + random.nextFloat() * 0.5f));
2530 numLost = actualCargoFractionLost * stack.stack.getSize();
2531 numTaken = Math.round(numLost * (0.5f + random.nextFloat() * 0.5f));
2535 if (random.nextFloat() < numLost) {
2543 if (numLost <= 0)
continue;
2545 stack.stack.add(-numLost);
2546 if (numTaken * recoveryFraction >= 1) {
2547 loot.addItems(stack.stack.getType(), stack.stack.getData(), numTaken * recoveryFraction);
2551 for (CampaignFleetAPI fleet :
battle.getSideFor(loser.getFleet())) {
2552 if (fleet.isPlayerFleet()) {
2553 fleet.getCargo().sort();
2564 protected void lootHullMods(FleetMemberAPI member, ShipVariantAPI variant,
float mult) {
2565 if (variant ==
null)
return;
2566 if (member.isFighterWing())
return;
2567 Random random = Misc.random;
2568 if (salvageRandom !=
null) random = salvageRandom;
2572 for (String
id : variant.getHullMods()) {
2573 if (random.nextFloat() > mult)
continue;
2574 if (random.nextFloat() > p)
continue;
2577 boolean known =
Global.
getSector().getPlayerFaction().knowsHullMod(
id);
2579 if (known || spec.isHidden() || spec.isHiddenEverywhere())
continue;
2581 if (spec.hasTag(Tags.HULLMOD_NO_DROP))
continue;
2583 loot.addHullmods(
id, 1);
2586 for (String slotId : variant.getModuleSlots()) {
2587 WeaponSlotAPI slot = variant.getSlot(slotId);
2588 if (slot.isStationModule()) {
2589 ShipVariantAPI module = variant.getModuleVariant(slotId);
2590 if (module ==
null)
continue;
2596 protected void lootWings(FleetMemberAPI member, ShipVariantAPI variant,
boolean own,
float mult) {
2597 if (variant ==
null)
return;
2598 if (member.isFighterWing())
return;
2599 Random random = Misc.random;
2600 if (salvageRandom !=
null) random = salvageRandom;
2605 p =
Global.
getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.OWN_WING_RECOVERY_MOD, p);
2607 p =
Global.
getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.ENEMY_WING_RECOVERY_MOD, p);
2612 for (String
id : variant.getNonBuiltInWings()) {
2613 if (!alreadyStripped) {
2614 if (random.nextFloat() > mult)
continue;
2615 if (random.nextFloat() > p)
continue;
2619 if (spec.hasTag(Tags.WING_NO_DROP))
continue;
2620 loot.addItems(CargoItemType.FIGHTER_CHIP,
id, 1);
2623 for (String slotId : variant.getModuleSlots()) {
2624 WeaponSlotAPI slot = variant.getSlot(slotId);
2625 if (slot.isStationModule()) {
2626 ShipVariantAPI module = variant.getModuleVariant(slotId);
2627 if (module ==
null)
continue;
2633 protected void lootWeapons(FleetMemberAPI member, ShipVariantAPI variant,
boolean own,
float mult,
boolean lootingModule) {
2634 if (variant ==
null)
return;
2635 if (member.isFighterWing())
return;
2641 if (own && !lootingModule && member.getCaptain() !=
null &&
2642 member.getCaptain().getMemoryWithoutUpdate().contains(
"$aiCoreIdForRecovery") &&
2644 !Misc.isUnremovable(member.getCaptain())) {
2646 loot.addItems(CargoItemType.RESOURCES,
2647 member.getCaptain().getMemoryWithoutUpdate().getString(
"$aiCoreIdForRecovery"), 1);
2650 Random random = Misc.random;
2651 if (salvageRandom !=
null) random = salvageRandom;
2653 if (!own && !lootingModule &&
2654 member.getCaptain().isAICore() && !variant.hasTag(Tags.VARIANT_DO_NOT_DROP_AI_CORE_FROM_CAPTAIN)) {
2655 String cid = member.getCaptain().getAICoreId();
2658 if (!spec.hasTag(Tags.NO_DROP)) {
2660 if (member.isStation()) {
2662 }
else if (member.isFrigate()) {
2664 }
else if (member.isDestroyer()) {
2666 }
else if (member.isCruiser()) {
2668 }
else if (member.isCapital()) {
2671 if (prob > 0 && random.nextFloat() < prob) {
2672 loot.addItems(CargoItemType.RESOURCES, cid, 1);
2682 p =
Global.
getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.OWN_WEAPON_RECOVERY_MOD, p);
2684 p =
Global.
getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.ENEMY_WEAPON_RECOVERY_MOD, p);
2689 Set<String>
remove =
new HashSet<String>();
2692 if (variant.hasTag(Tags.VARIANT_CONSISTENT_WEAPON_DROPS)) {
2693 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
2694 String weaponId = variant.getWeaponId(slotId);
2695 if (weaponId ==
null)
continue;
2696 if (
loot.getNumWeapons(weaponId) <= 0) {
2698 if (spec.hasTag(Tags.NO_DROP))
continue;
2700 loot.addWeapons(weaponId, 1);
2706 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
2707 if (
remove.contains(slotId))
continue;
2709 if (!alreadyStripped) {
2710 if (random.nextFloat() > mult)
continue;
2711 if (random.nextFloat() > p)
continue;
2714 String weaponId = variant.getWeaponId(slotId);
2716 if (spec.hasTag(Tags.NO_DROP))
continue;
2718 loot.addItems(CargoAPI.CargoItemType.WEAPONS, weaponId, 1);
2723 for (String slotId : variant.getModuleSlots()) {
2724 WeaponSlotAPI slot = variant.getSlot(slotId);
2725 if (slot.isStationModule()) {
2726 ShipVariantAPI module = variant.getModuleVariant(slotId);
2727 if (module ==
null)
continue;
2742 if (winner ==
null || loser ==
null)
return;
2744 List<CampaignFleetAPI> winners =
battle.getSideFor(winner.getFleet());
2745 WeightedRandomPicker<CampaignFleetAPI> picker =
new WeightedRandomPicker<CampaignFleetAPI>();
2746 for (CampaignFleetAPI curr : winners) {
2747 picker.add(curr, curr.getFleetPoints());
2749 for (CargoStackAPI stack :
loot.getStacksCopy()) {
2750 if (stack.isNull() || stack.isFuelStack())
continue;
2752 CampaignFleetAPI pick = picker.pick();
2753 if (pick ==
null)
break;
2755 CargoAPI winnerCargo = pick.getCargo();
2756 float spaceLeft = winnerCargo.getSpaceLeft();
2757 if (spaceLeft <= 0) {
2758 picker.remove(pick);
2762 float spacePerUnit = stack.getCargoSpacePerUnit();
2763 float maxUnits = (int) (spaceLeft / spacePerUnit);
2764 if (maxUnits > stack.getSize()) maxUnits = stack.getSize();
2765 maxUnits = Math.round(maxUnits * (Math.random() * 0.5f + 0.5f));
2766 winnerCargo.addItems(stack.getType(), stack.getData(), maxUnits);
2770 for (CampaignFleetAPI curr : winners) {
2771 picker.add(curr, curr.getFleetPoints());
2773 for (CargoStackAPI stack :
loot.getStacksCopy()) {
2774 if (stack.isNull() || !stack.isFuelStack())
continue;
2776 CampaignFleetAPI pick = picker.pick();
2777 if (pick ==
null)
break;
2779 CargoAPI winnerCargo = pick.getCargo();
2780 float spaceLeft = winnerCargo.getMaxCapacity() - winnerCargo.getFuel();
2781 if (spaceLeft <= 0) {
2782 picker.remove(pick);
2786 float spacePerUnit = stack.getCargoSpacePerUnit();
2787 float maxUnits = (int) (spaceLeft / spacePerUnit);
2788 if (maxUnits > stack.getSize()) maxUnits = stack.getSize();
2789 maxUnits = Math.round(maxUnits * (Math.random() * 0.5f + 0.5f));
2790 winnerCargo.addItems(stack.getType(), stack.getData(), maxUnits);
2806 return fleet.getFleetData().getMinBurnLevel() >= other.getFleetData().getMaxBurnLevel() + 1f;
2820 Set<CampaignFleetAPI> fleetsWithDecks =
new HashSet<CampaignFleetAPI>();
2821 for (FleetMemberAPI curr : result.getReserves()) {
2822 if (
battle.getSourceFleet(curr) ==
null)
continue;
2823 if (curr.isMothballed())
continue;
2824 if (curr.getNumFlightDecks() > 0) {
2825 fleetsWithDecks.add(
battle.getSourceFleet(curr));
2828 for (FleetMemberAPI curr : result.getDeployed()) {
2829 if (
battle.getSourceFleet(curr) ==
null)
continue;
2830 if (curr.isMothballed())
continue;
2831 if (curr.getNumFlightDecks() > 0) {
2832 fleetsWithDecks.add(
battle.getSourceFleet(curr));
2835 for (FleetMemberAPI curr : result.getRetreated()) {
2836 if (
battle.getSourceFleet(curr) ==
null)
continue;
2837 if (curr.isMothballed())
continue;
2838 if (curr.getNumFlightDecks() > 0) {
2839 fleetsWithDecks.add(
battle.getSourceFleet(curr));
2843 List<FleetMemberAPI> saved =
new ArrayList<FleetMemberAPI>();
2844 for (FleetMemberAPI curr : result.getDestroyed()) {
2845 if (
battle.getSourceFleet(curr) ==
null)
continue;
2846 if (!fleetsWithDecks.contains(
battle.getSourceFleet(curr)))
continue;
2847 if (curr.isFighterWing()) {
2852 result.getDestroyed().removeAll(saved);
2853 result.getRetreated().addAll(saved);
2856 List<FleetMemberAPI> toRepair =
new ArrayList<FleetMemberAPI>();
2857 toRepair.addAll(result.getDeployed());
2858 toRepair.addAll(result.getRetreated());
2859 for (FleetMemberAPI curr : toRepair) {
2860 if (
battle.getSourceFleet(curr) ==
null)
continue;
2861 if (curr.isFighterWing()) {
2862 if (fleetsWithDecks.contains(
battle.getSourceFleet(curr))) {
2863 curr.getStatus().repairFully();
2865 curr.getStatus().repairFullyNoNewFighters();
2873 EngagementResultForFleetAPI winner = result.getWinnerResult();
2874 EngagementResultForFleetAPI loser = result.getLoserResult();
2877 boolean playerInvolved =
battle.isPlayerInvolved();
2886 EngagementResultForFleetAPI winner = result.getWinnerResult();
2887 EngagementResultForFleetAPI loser = result.getLoserResult();
2898 boolean wonBattle = result.isWinner();
2901 for (FleetMemberAPI member : result.getFleet().getFleetData().getMembersListCopy()) {
2906 List<FleetMemberAPI> applyDeployCostTo =
new ArrayList<FleetMemberAPI>(result.getDeployed());
2908 for (FleetMemberAPI member : result.getDisabled()) {
2913 member.getRepairTracker().applyCREvent(-1f * mult,
"disabled in combat");
2916 applyDeployCostTo.add(member);
2919 for (FleetMemberAPI member : result.getDestroyed()) {
2926 member.getRepairTracker().applyCREvent(-1f * mult,
"disabled in combat");
2929 applyDeployCostTo.add(member);
2933 for (FleetMemberAPI member : applyDeployCostTo) {
2935 if (member.isFighterWing()) {
2936 member.getRepairTracker().applyCREvent(-deployCost,
"wing deployed in combat");
2938 member.getRepairTracker().applyCREvent(-deployCost,
"deployed in combat");
2946 for (FleetMemberAPI member : result.getRetreated()) {
2948 if (member.isFighterWing()) {
2949 member.getRepairTracker().applyCREvent(-deployCost,
"wing deployed in combat");
2951 member.getRepairTracker().applyCREvent(-deployCost,
"deployed in combat");
2956 if (!wonBattle && result.getGoal() != FleetGoal.ESCAPE) {
2957 float retreatCost = deployCost * retreatLossMult;
2958 if (retreatCost > 0) {
2959 member.getRepairTracker().applyCREvent(-retreatCost,
"retreated from lost engagement");
2988 DeployedFleetMemberAPI dfm =
getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
2989 if (dfm ==
null)
return;
2991 if (
battle !=
null &&
battle.getSourceFleet(member) ==
null) {
2994 if (member.getFleetCommander() ==
null) {
2998 if (dfm.getMember() == member && dfm.isFighterWing()) {
3000 float cr = member.getRepairTracker().getBaseCR();
3003 member.getRepairTracker().applyCREvent(-(cr - finalCR),
"deployed replacement chassis in combat");
3007 if (dfm.getMember() == member && !dfm.isFighterWing()) {
3009 float endOfCombatCR = dfm.getShip().getCurrentCR() - deployCost;
3010 float cr = member.getRepairTracker().getCR();
3011 if (cr > endOfCombatCR) {
3012 member.getRepairTracker().applyCREvent(-(cr - endOfCombatCR),
"extended deployment");
3015 ShipAPI ship = dfm.getShip();
3016 if (dfm.getShip() !=
null && !dfm.isFighterWing()) {
3022 float hullDamageFraction = ship.getHullLevelAtDeployment() - ship.getLowestHullLevelReached();
3023 float hullDamageCRLoss = hullDamageFraction * hMult;
3024 hullDamageCRLoss *= ship.getMutableStats().getDynamic().getValue(Stats.HULL_DAMAGE_CR_LOSS);
3025 if (hullDamageCRLoss > 0) {
3026 member.getRepairTracker().applyCREvent(-hullDamageCRLoss,
"hull damage sustained");
3029 member.getStatus().setHullFraction(ship.getLowestHullLevelReached());
3032 float instaRepairFraction = member.getStats().getDynamic().getValue(Stats.INSTA_REPAIR_FRACTION, 0f);
3033 if (instaRepairFraction > 0) {
3034 float hullDamage = member.getStatus().getHullDamageTaken();
3035 float armorDamage = member.getStatus().getArmorDamageTaken();
3037 member.getStatus().repairArmorAllCells(armorDamage * instaRepairFraction);
3038 member.getStatus().repairHullFraction(hullDamage * instaRepairFraction);
3042 float totalDisabled = 0f;
3043 MutableCharacterStatsAPI stats = member.getFleetCommander().getStats();
3044 float maxOP = ship.getVariant().getHullSpec().getOrdnancePoints(stats);
3045 if (maxOP <= 1) maxOP = 1;
3047 for (WeaponAPI w : ship.getDisabledWeapons()) {
3048 totalDisabled += w.getSpec().getOrdnancePointCost(stats, ship.getVariant().getStatsForOpCosts()) * wMult;
3050 if (ship.getNumFlameouts() > 0) {
3051 totalDisabled += maxOP * eMult;
3054 float damageBasedCRLoss = Math.min(1f, totalDisabled / maxOP);
3055 if (damageBasedCRLoss > 0) {
3056 member.getRepairTracker().applyCREvent(-damageBasedCRLoss,
"weapon and engine damage sustained");
3059 float missileReloadOP = 0f;
3060 for (WeaponAPI w : ship.getAllWeapons()) {
3061 if (w.getType() == WeaponType.MISSILE && w.usesAmmo()) {
3062 missileReloadOP += (1f - (float) w.getAmmo() / (float) w.getMaxAmmo()) * w.getSpec().getOrdnancePointCost(stats, ship.getVariant().getStatsForOpCosts()) * mMult;
3066 float missileReloadLoss = Math.min(1f, missileReloadOP / maxOP);
3067 if (missileReloadLoss > 0) {
3068 member.getRepairTracker().applyCREvent(-missileReloadLoss,
"missile weapons used in combat");
3078 for (FleetMemberAPI member : result.getDestroyed()) {
3079 if (
battle.getSourceFleet(member) ==
null)
continue;
3080 battle.getSourceFleet(member).removeFleetMemberWithDestructionFlash(member);
3081 result.getFleet().getFleetData().removeFleetMember(member);
3083 for (FleetMemberAPI member : result.getDisabled()) {
3084 if (
battle.getSourceFleet(member) ==
null)
continue;
3085 battle.getSourceFleet(member).removeFleetMemberWithDestructionFlash(member);
3086 result.getFleet().getFleetData().removeFleetMember(member);
3103 protected float computeLossFraction(FleetMemberAPI member, EngagementResultForFleetAPI result,
float hullFraction,
float hullDamage) {
3104 if (member ==
null && hullFraction == 0) {
3105 return (0.75f + (
float) Math.random() * 0.25f);
3109 if (member.isFighterWing() && result !=
null) {
3111 float extraLossMult = hullDamage;
3112 DeployedFleetMemberAPI dfm =
getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
3113 if (dfm !=
null && dfm.getMember() == member) {
3115 float cr = member.getRepairTracker().getCR();
3118 float crPer = dfm.getMember().getStats().getCRPerDeploymentPercent().computeEffective(dfm.getMember().getVariant().getHullSpec().getCRToDeploy()) / 100f;
3119 float extraCraftLost = (cr - finalCR) / crPer;
3120 float wingSize = dfm.getMember().getNumFightersInWing();
3121 if (extraCraftLost >= 1) {
3122 extraLossMult = hullDamage + extraCraftLost / wingSize;
3126 return (0.25f + (
float) Math.random() * 0.75f * (float) Math.random()) * member.getStats().getCrewLossMult().getModifiedValue() * extraLossMult;
3130 float extraFromFighters = 0f;
3131 if (!member.isFighterWing() && result !=
null) {
3132 DeployedFleetMemberAPI dfm =
getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
3133 if (dfm !=
null && dfm.getMember() == member) {
3134 float craftCrewLoss = 0;
3135 for (FighterLaunchBayAPI bay : dfm.getShip().getLaunchBaysCopy()) {
3136 if (bay.getWing() ==
null || bay.getWing().getLeader() ==
null)
continue;
3137 float baseCrew = bay.getWing().getLeader().getHullSpec().getMinCrew();
3138 float perCraft = bay.getWing().getLeader().getMutableStats().getMinCrewMod().computeEffective(baseCrew);
3139 perCraft *= bay.getWing().getLeader().getMutableStats().getDynamic().getValue(Stats.FIGHTER_CREW_LOSS_MULT);
3140 craftCrewLoss += perCraft * bay.getNumLost();
3144 craftCrewLoss *= baseLossFraction;
3146 float memberCrew = member.getMinCrew();
3147 if (memberCrew > 0) {
3148 float threshold = memberCrew * 0.33f;
3150 float actualLost = 0f;
3153 float curr = Math.min(craftCrewLoss, threshold);
3154 craftCrewLoss -= curr;
3160 }
while (craftCrewLoss > 0);
3162 extraFromFighters = actualLost / memberCrew;
3163 extraFromFighters *= member.getStats().getDynamic().getValue(Stats.FIGHTER_CREW_LOSS_MULT);
3168 if (hullFraction == 0) {
3169 return Math.min(1f, (0.75f + (
float) Math.random() * 0.25f) * member.getStats().getCrewLossMult().getModifiedValue() + extraFromFighters);
3171 return Math.min(1f, hullDamage * hullDamage * (0.5f + (
float) Math.random() * 0.5f) * member.getStats().getCrewLossMult().getModifiedValue() + extraFromFighters);
3183 boolean wonBattle = result.isWinner();
3185 DataForEncounterSide data =
getDataFor(result.getFleet());
3186 CrewCompositionAPI recoverable = data.getRecoverableCrewLosses();
3189 List<FleetMemberAPI> all =
new ArrayList<FleetMemberAPI>();
3190 all.addAll(result.getDisabled());
3191 all.addAll(result.getDeployed());
3192 all.addAll(result.getDestroyed());
3193 all.addAll(result.getRetreated());
3194 all.addAll(result.getReserves());
3196 for (FleetMemberAPI member : result.getReserves()) {
3197 member.getStatus().resetDamageTaken();
3200 CrewCompositionAPI playerLosses = data.getCrewLossesDuringLastEngagement();
3201 playerLosses.removeAllCrew();
3204 crewLosses.removeAllCrew();
3206 CampaignFleetAPI playerFleet =
null;
3208 float playerCapacityLost = 0f;
3209 for (FleetMemberAPI member : all) {
3210 if (
battle.getSourceFleet(member) ==
null)
continue;
3211 boolean player =
battle.getSourceFleet(member) !=
null &&
battle.getSourceFleet(member).isPlayerFleet();
3212 CrewCompositionAPI c = member.getCrewComposition();
3214 float hullDamage = member.getStatus().getHullDamageTaken();
3215 float hullFraction = member.getStatus().getHullFraction();
3216 member.getStatus().resetDamageTaken();
3230 if (result.getDisabled().contains(member) || result.getDestroyed().contains(member)) {
3231 if (playerInvolved &&
3232 !
battle.getPlayerSide().contains(
battle.getSourceFleet(member))) {
3237 recoverable.addCrew((1f - f1) * c.getCrew());
3239 playerLosses.addCrew(c.getCrew() * 1f);
3240 playerCapacityLost += member.getMaxCrew();
3243 crewLosses.addCrew(c.getCrew() * 1f);
3253 crewLosses.addCrew((f1 - 1) * c.getCrew());
3256 float lost = c.getCrew() * f1;
3258 c.transfer(lost, crewLosses);
3261 playerLosses.addCrew(lost);
3262 playerCapacityLost += member.getMaxCrew() * f1;
3266 if (
battle.getSourceFleet(member).isPlayerFleet() &&
3267 (crewLosses.getCrew() > 0 || crewLosses.getMarines() > 0)) {
3268 playerFleet =
battle.getSourceFleet(member);
3271 CargoAPI cargo =
battle.getSourceFleet(member).getCargo();
3272 cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, (
int)crewLosses.getCrew());
3273 cargo.removeMarines((
int) crewLosses.getMarines());
3278 if (playerFleet !=
null) {
3279 playerFleet.getFleetData().updateCargoCapacities();
3280 CargoAPI cargo = playerFleet.getCargo();
3281 float maxCrew = cargo.getMaxPersonnel();
3282 float totalCrew = cargo.getTotalCrew();
3283 float marines = cargo.getMarines();
3284 float recoverableTotal = recoverable.getCrew() + recoverable.getMarines();
3288 float total = totalCrew + marines + recoverableTotal;
3289 if (maxCrew + playerCapacityLost > 0) {
3290 total *= playerCapacityLost / (maxCrew + playerCapacityLost);
3297 if (total > maxCrew) {
3299 float toLose = total - maxCrew;
3302 recoverable.transfer(Math.min(recoverableTotal, toLose),
null);
3303 toLose -= recoverableTotal;
3304 total -= recoverableTotal;
3306 if (toLose > 0 && total > 0) {
3307 float crew = cargo.getCrew();
3310 crewLosses.addCrew((
int)Math.ceil(crew / total * toLose));
3311 crewLosses.addMarines((
int)Math.ceil(marines / total * toLose));
3313 playerLosses.addCrew(crewLosses.getCrew() * 1f);
3314 playerLosses.addMarines(crewLosses.getMarines() * 1f);
3316 cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, (
int)crewLosses.getCrew());
3317 cargo.removeMarines((
int) crewLosses.getMarines());
3328 DataForEncounterSide data =
getDataFor(fleet);
3330 CrewCompositionAPI rec = data.getRecoverableCrewLosses();
3332 cargo.addItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, rec.getCrew());
3334 cargo.addMarines((
int) rec.getMarines());
3369 float scorePlayer = 0f;
3370 float scoreEnemy = 0f;
3372 float officerBase = 30;
3373 float officerPerLevel = 15;
3375 float baseMult = 2f;
3376 float dModMult = 0.9f;
3378 for (FleetMemberAPI member :
battle.getNonPlayerCombined().getFleetData().getMembersListCopy()) {
3379 if (member.isMothballed())
continue;
3380 float mult = baseMult;
3381 if (member.isStation()) mult *= 2f;
3382 else if (member.isCivilian()) mult *= 0.25f;
3383 if (member.getCaptain() !=
null && !member.getCaptain().isDefault()) {
3384 scoreEnemy += officerBase + officerPerLevel * Math.max(1f, member.getCaptain().getStats().getLevel());
3387 for (
int i = 0; i < dMods; i++) {
3390 scoreEnemy += member.getUnmodifiedDeploymentPointsCost() * mult;
3392 scoreEnemy *= 0.67f;
3394 float maxPlayserShipScore = 0f;
3396 officerBase *= 0.5f;
3397 officerPerLevel *= 0.5f;
3398 Set<PersonAPI> seenOfficers =
new HashSet<PersonAPI>();
3399 int unofficeredShips = 0;
3400 for (FleetMemberAPI member :
battle.getPlayerCombined().getFleetData().getMembersListCopy()) {
3401 if (member.isMothballed())
continue;
3402 float mult = baseMult;
3403 if (member.isStation()) mult *= 2f;
3404 else if (member.isCivilian()) mult *= 0.25f;
3405 if (member.getCaptain() !=
null && !member.getCaptain().isDefault()) {
3406 scorePlayer += officerBase + officerPerLevel * Math.max(1f, member.getCaptain().getStats().getLevel());
3407 seenOfficers.add(member.getCaptain());
3408 }
else if (!member.isCivilian()) {
3412 for (
int i = 0; i < dMods; i++) {
3415 float currShipBaseScore = member.getUnmodifiedDeploymentPointsCost() * mult;
3416 scorePlayer += currShipBaseScore;
3417 if (
battle.getSourceFleet(member) !=
null &&
battle.getSourceFleet(member).isPlayerFleet()) {
3418 maxPlayserShipScore = Math.max(maxPlayserShipScore, currShipBaseScore);
3425 for (OfficerDataAPI od :
Global.
getSector().getPlayerFleet().getFleetData().getOfficersCopy()) {
3426 if (seenOfficers.contains(od.getPerson()))
continue;
3427 if (od.getPerson().isPlayer())
continue;
3428 if (unofficeredShips <= 0)
break;
3430 scorePlayer += officerBase + officerPerLevel * Math.max(1f, od.getPerson().getStats().getLevel());
3433 scorePlayer = Math.max(scorePlayer, Math.min(scoreEnemy * 0.5f, maxPlayserShipScore * 6f));
3435 if (scorePlayer < 1) scorePlayer = 1;
3436 if (scoreEnemy < 1) scoreEnemy = 1;
static SettingsAPI getSettings()
static FactoryAPI getFactory()
static SectorAPI getSector()
static void addDMods(FleetMemberData data, boolean own, CampaignFleetAPI recoverer, Random random)
static boolean setDHull(ShipVariantAPI variant)
static int getNumDMods(ShipVariantAPI variant)
static boolean ALLOW_KNOWN_HULLMOD_DROPS
List< FleetMemberAPI > getRecoverableShips(BattleAPI battle, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet)
boolean playerOnlyRetreated
void lootWings(FleetMemberAPI member, ShipVariantAPI variant, boolean own, float mult)
float computeLossFraction(FleetMemberAPI member, EngagementResultForFleetAPI result, float hullFraction, float hullDamage)
float performPostVictoryRecovery(EngagementResultForFleetAPI winnerResult, EngagementResultForFleetAPI loserResult)
void generateLoot(List< FleetMemberAPI > recoveredShips, boolean withCredits)
void addPotentialOfficer()
boolean engagedInActualBattle
boolean didPlayerWinMostRecentBattleOfEncounter()
float getSalvageMult(Status status)
void applyShipLosses(EngagementResultAPI result)
void tallyOfficerTime(DataForEncounterSide data, EngagementResultForFleetAPI result)
void setNoHarryBecauseOfStation(boolean noHarryBecauseOfStation)
void applyCREffect(EngagementResultForFleetAPI result)
void gainOfficerXP(DataForEncounterSide data, float xp)
void setAllyFPHullDamageToEnemies(float allyFPHullDamageToEnemies)
void setSalvageRandom(Random salvageRandom)
static final float ENGAGE_DISABLE_CHANCE
void setTextPanelForXPGain(TextPanelAPI textPanelForXPGain)
DataForEncounterSide getLoserData()
void lootHullMods(FleetMemberAPI member, ShipVariantAPI variant, float mult)
boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText)
boolean wasLastEngagementEscape()
List< FleetMemberAPI > recoverableShips
float getCargoLootMult(Status status)
float playerFPHullDamageToEnemies
void setDifficulty(float difficulty)
void applyPursuitOption(CampaignFleetAPI pursuingFleet, CampaignFleetAPI otherFleet, PursuitOption pursuitOption)
static final float ENGAGE_DESTROY_CHANCE
Map< FactionAPI, Float > playerFPHullDamageToAlliesByFaction
void applyAfterBattleEffectsIfThereWasABattle()
float getAllyFPHullDamageToEnemies()
boolean isOtherFleetHarriedPlayer()
float computePlayerContribFraction()
boolean isNoHarryBecauseOfStation()
CampaignFleetAPI getWinner()
boolean isEngagedInActualBattle()
void gainXP(DataForEncounterSide side, DataForEncounterSide otherSide)
static final float DOCK_SUCCESS_CHANCE
boolean playerDidSeriousDamage
float performPostEngagementRecoveryBoth(EngagementResultAPI result)
boolean didPlayerWinEncounterOutright()
boolean otherFleetHarriedPlayer
BoardingResult boardShip(FleetMemberAPI member, CampaignFleetAPI attacker, CampaignFleetAPI defender)
static void prepareModuleForRecovery(FleetMemberAPI member, String moduleSlotId, boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods, float weaponRetainProb, float wingRetainProb, Random salvageRandom)
void setOtherFleetHarriedPlayer(boolean otherFleetHarriedPlayer)
void setComputedDifficulty(boolean computedDifficulty)
void updateDeployedMap(EngagementResultForFleetAPI result)
List< FleetMemberAPI > getStoryRecoverableShips()
void recoverCrew(CampaignFleetAPI fleet)
float computeBattleDifficulty()
float getBoardingSuccessPercent(FleetMemberAPI member, CampaignFleetAPI attacker, CampaignFleetAPI defender)
static final float ENGAGE_ESCAPE_CHANCE
static void prepareShipForRecovery(FleetMemberAPI member, boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods, float weaponRetainProb, float wingRetainProb, Random salvageRandom)
boolean alreadyAdjustedRep
DataForEncounterSide getDataFor(CampaignFleetAPI participantOrCombined)
EngagementOutcome lastOutcome
static final float CIV_SELF_DESTRUCT_CHANCE
static void recoverShips(List< FleetMemberAPI > ships, FleetEncounterContext context, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet)
Random getSalvageRandom()
void computeMissedLaunchLosses(BoardingResult result, CrewCompositionAPI boardingParty)
Map< FleetMemberAPI, Float > preEngagementCRForWinner
float getSalvageValueModPlayerShips()
PursueAvailability getPursuitAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet)
void clearNoSourceMembers(EngagementResultForFleetAPI result)
void computeCrewLossFromBoarding(BoardingResult result, FleetMemberAPI member, CrewCompositionAPI boardingParty, float attackerStr, float defenderStr)
float performPostVictoryRecovery(EngagementResultAPI result)
void handleCargoLooting(List< FleetMemberAPI > recoveredShips, boolean takingFromPlayer)
void setAutoresolve(boolean isAutoresolve)
void applyShipLosses(EngagementResultForFleetAPI result)
TextPanelAPI textPanelForXPGain
boolean hasWinnerAndLoser()
static final float LAUNCH_SUCCESS_CHANCE
boolean canOutrunOtherFleet(CampaignFleetAPI fleet, CampaignFleetAPI other)
void applyBoardingSelfDestruct(FleetMemberAPI member, CrewCompositionAPI boardingParty, BoardingAttackType attackType, List< FleetMemberAPI > boardingTaskForce, CampaignFleetAPI attacker, CampaignFleetAPI defender, BoardingResult result)
void applyExtendedCRLossIfNeeded(EngagementResultForFleetAPI result, FleetMemberAPI member)
void applyCrewLosses(EngagementResultAPI result)
boolean isEngagedInHostilities()
EngageBoardableOutcome engageBoardableShip(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet)
void setEngagedInActualBattle(boolean engagedInActualBattle)
void computeFPHullDamage()
boolean isComputedDifficulty()
void setPlayerFPHullDamageToEnemies(float playerFPHullDamageToEnemies)
float playerFPHullDamageToAllies
CombatDamageData runningDamageTotal
boolean engagedInHostilities
boolean computedDifficulty
void applyResultToFleets(EngagementResultAPI result)
boolean noHarryBecauseOfStation
CampaignFleetAPI getLoser()
List< DataForEncounterSide > sideData
TextPanelAPI getTextPanelForXPGain()
boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText, boolean okToAdjustAlly, boolean okToAdjustEnemy)
static final float SELF_DESTRUCT_CHANCE
List< FleetMemberAPI > storyRecoverableShips
DataForEncounterSide getWinnerData()
EngagementOutcome getLastEngagementOutcome()
DisengageHarryAvailability getDisengageHarryAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet)
float getPlayerFPHullDamageToEnemies()
void fixFighters(EngagementResultForFleetAPI result)
float getDeployCost(FleetMemberAPI member)
void generatePlayerLoot(List< FleetMemberAPI > recoveredShips, boolean withCredits)
float allyFPHullDamageToEnemies
void calculateAndApplyCrewLosses(EngagementResultForFleetAPI result, boolean playerInvolved)
static final float LAUNCH_CLEAN_ESCAPE_CHANCE
void lootWeapons(FleetMemberAPI member, ShipVariantAPI variant, boolean own, float mult, boolean lootingModule)
void setBattle(BattleAPI battle)
boolean didPlayerWinLastEngagement()
void processEngagementResults(EngagementResultAPI result)
void letBoardableGo(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet)
void setEngagedInHostilities(boolean engagedInHostilities)
float computeRecoverableFraction(FleetMemberAPI member, EngagementResultForFleetAPI result, float hullFraction, float hullDamage)
static float getAdjustedGantryModifierForPostCombatSalvage(CampaignFleetAPI fleet)
CargoAPI createCargo(boolean unlimitedStacks)
CrewCompositionAPI createCrewComposition()
HullModSpecAPI getHullModSpec(String modId)
CommoditySpecAPI getCommoditySpec(String commodityId)
float getFloat(String key)
FighterWingSpecAPI getFighterWingSpec(String wingId)
WeaponSpecAPI getWeaponSpec(String weaponId)