Starsector API
Loading...
Searching...
No Matches
FleetEncounterContext.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.Iterator;
9import java.util.LinkedHashMap;
10import java.util.List;
11import java.util.Map;
12import java.util.Random;
13import java.util.Set;
14
15import com.fs.starfarer.api.Global;
16import com.fs.starfarer.api.campaign.BattleAPI;
17import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
18import com.fs.starfarer.api.campaign.CampaignFleetAPI;
19import com.fs.starfarer.api.campaign.CargoAPI;
20import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
21import com.fs.starfarer.api.campaign.CargoStackAPI;
22import com.fs.starfarer.api.campaign.CombatDamageData;
23import com.fs.starfarer.api.campaign.CombatDamageData.DamageToFleetMember;
24import com.fs.starfarer.api.campaign.CombatDamageData.DealtByFleetMember;
25import com.fs.starfarer.api.campaign.EngagementResultForFleetAPI;
26import com.fs.starfarer.api.campaign.FactionAPI;
27import com.fs.starfarer.api.campaign.FleetAssignment;
28import com.fs.starfarer.api.campaign.FleetDataAPI;
29import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin;
30import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.DataForEncounterSide.OfficerEngagementData;
31import com.fs.starfarer.api.campaign.InteractionDialogAPI;
32import com.fs.starfarer.api.campaign.TextPanelAPI;
33import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.PursuitOption;
34import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
35import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
36import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
37import com.fs.starfarer.api.campaign.rules.MemoryAPI;
38import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
39import com.fs.starfarer.api.characters.OfficerDataAPI;
40import com.fs.starfarer.api.characters.PersonAPI;
41import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
42import com.fs.starfarer.api.combat.EngagementResultAPI;
43import com.fs.starfarer.api.combat.FighterLaunchBayAPI;
44import com.fs.starfarer.api.combat.ShipAPI;
45import com.fs.starfarer.api.combat.ShipVariantAPI;
46import com.fs.starfarer.api.combat.WeaponAPI;
47import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
48import com.fs.starfarer.api.fleet.CrewCompositionAPI;
49import com.fs.starfarer.api.fleet.FleetGoal;
50import com.fs.starfarer.api.fleet.FleetMemberAPI;
51import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
52import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
53import com.fs.starfarer.api.impl.campaign.ids.Commodities;
54import com.fs.starfarer.api.impl.campaign.ids.Factions;
55import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
56import com.fs.starfarer.api.impl.campaign.ids.Stats;
57import com.fs.starfarer.api.impl.campaign.ids.Tags;
58import com.fs.starfarer.api.impl.campaign.intel.PromoteOfficerIntel;
59import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData;
60import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageEntity;
61import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BaseSalvageSpecial;
62import com.fs.starfarer.api.impl.campaign.tutorial.TutorialMissionIntel;
63import com.fs.starfarer.api.loading.FighterWingSpecAPI;
64import com.fs.starfarer.api.loading.HullModSpecAPI;
65import com.fs.starfarer.api.loading.VariantSource;
66import com.fs.starfarer.api.loading.WeaponGroupSpec;
67import com.fs.starfarer.api.loading.WeaponSlotAPI;
68import com.fs.starfarer.api.loading.WeaponSpecAPI;
69import com.fs.starfarer.api.util.Misc;
70import com.fs.starfarer.api.util.WeightedRandomPicker;
71
73
74 protected List<DataForEncounterSide> sideData = new ArrayList<DataForEncounterSide>();
75 protected boolean engagedInHostilities = false;
76 protected boolean engagedInActualBattle = false;
77 protected boolean playerOnlyRetreated = true;
78 protected boolean playerPursued = false;
79 protected boolean playerDidSeriousDamage = false;
80 protected BattleAPI battle;
81 protected boolean otherFleetHarriedPlayer = false;
82 protected boolean ongoingBattle = false;
83
84 protected boolean isAutoresolve = false;
85
86
88
89 protected Map<FleetMemberAPI, CampaignFleetAPI> origSourceForRecoveredShips = new LinkedHashMap<>();
90
92
93 }
94 public boolean isAutoresolve() {
95 return isAutoresolve;
96 }
97
98 public void setAutoresolve(boolean isAutoresolve) {
99 this.isAutoresolve = isAutoresolve;
100 }
101
102
104 return battle;
105 }
106
108 this.battle = battle;
109 }
110
111 public DataForEncounterSide getDataFor(CampaignFleetAPI participantOrCombined) {
112 CampaignFleetAPI combined = battle.getCombinedFor(participantOrCombined);
113 if (combined == null) {
114 return new DataForEncounterSide(participantOrCombined);
115 }
116
117 for (DataForEncounterSide curr : sideData) {
118 if (curr.getFleet() == combined) return curr;
119 }
120 DataForEncounterSide dfes = new DataForEncounterSide(combined);
121 sideData.add(dfes);
122
123 return dfes;
124 }
125
126 public DataForEncounterSide getWinnerData() {
127 for (DataForEncounterSide curr : sideData) {
128 if (!curr.disengaged()) {
129 return curr;
130 }
131 }
132 return null;
133 }
134
135 public DataForEncounterSide getLoserData() {
136 for (DataForEncounterSide curr : sideData) {
137 if (curr.disengaged()) {
138 return curr;
139 }
140 }
141 return null;
142 }
143
144 public boolean isEngagedInHostilities() {
146 }
147
149 this.engagedInHostilities = engagedInHostilities;
150 }
151
153 this.otherFleetHarriedPlayer = otherFleetHarriedPlayer;
154 }
155
156 public boolean isOtherFleetHarriedPlayer() {
158 }
159
161 DataForEncounterSide data = getDataFor(result.getFleet());
162 data.getMemberToDeployedMap().clear();
163
164 List<DeployedFleetMemberAPI> deployed = result.getAllEverDeployedCopy();
165 if (deployed != null && !deployed.isEmpty()) {
166 for (DeployedFleetMemberAPI dfm : deployed) {
167 if (dfm.getMember() != null) {
168 FleetMemberAPI member = dfm.getMember();
169 data.getMemberToDeployedMap().put(member, dfm);
170
171 if (dfm != null && dfm.getShip() != null && dfm.getShip().getOriginalCaptain() != null &&
172 !dfm.getShip().getOriginalCaptain().isDefault()) {
173 data.getMembersWithOfficerOrPlayerAsOrigCaptain().add(member);
174 }
175 }
176 }
177 }
178 }
179
186 Iterator<FleetMemberAPI> iter = result.getDeployed().iterator();
187 while (iter.hasNext()) {
188 FleetMemberAPI member = iter.next();
189 if (battle.getSourceFleet(member) == null) {
190 iter.remove();
191 }
192 }
193 iter = result.getReserves().iterator();
194 while (iter.hasNext()) {
195 FleetMemberAPI member = iter.next();
196 if (battle.getSourceFleet(member) == null) {
197 iter.remove();
198 }
199 }
200 iter = result.getDestroyed().iterator();
201 while (iter.hasNext()) {
202 FleetMemberAPI member = iter.next();
203 if (battle.getSourceFleet(member) == null) {
204 iter.remove();
205 }
206 }
207 iter = result.getDisabled().iterator();
208 while (iter.hasNext()) {
209 FleetMemberAPI member = iter.next();
210 if (battle.getSourceFleet(member) == null) {
211 iter.remove();
212 }
213 }
214 iter = result.getRetreated().iterator();
215 while (iter.hasNext()) {
216 FleetMemberAPI member = iter.next();
217 if (battle.getSourceFleet(member) == null) {
218 iter.remove();
219 }
220 }
221 }
222
223 public boolean isEngagedInActualBattle() {
225 }
226
228 this.engagedInActualBattle = engagedInActualBattle;
229 }
230
233 engagedInActualBattle = true; // whether autoresolve or not, there was actual fighting
234
235 // the fleets we get back here are combined fleets from the BattleAPI,
236 // NOT the actual fleets involved, even if it's a 1 vs 1 battle.
237 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
238 EngagementResultForFleetAPI loserResult = result.getLoserResult();
239
240 clearNoSourceMembers(winnerResult);
241 clearNoSourceMembers(loserResult);
242
243 // only happens for combat where player is involved
244 CombatDamageData currDamageData = result.getLastCombatDamageData();
245 if (currDamageData != null) {
246 if (runningDamageTotal == null) {
247 runningDamageTotal = currDamageData;
248 } else {
249 runningDamageTotal.add(currDamageData);
250 }
252 }
253
254 //if (winnerResult.getFleet().isPlayerFleet() || loserResult.getFleet().isPlayerFleet()) {
255 if (battle.isPlayerInvolved()) {
257 }
258
259 updateDeployedMap(winnerResult);
260 updateDeployedMap(loserResult);
261
262 //result.applyToFleets();
263 applyResultToFleets(result);
264
265 //if (winnerResult.getFleet().isPlayerFleet() && winnerResult.getGoal() != FleetGoal.ESCAPE) {
266 if (battle.isPlayerSide(winnerResult) && winnerResult.getGoal() != FleetGoal.ESCAPE) {
267 playerOnlyRetreated = false;
268 if (loserResult.getGoal() == FleetGoal.ESCAPE) {
269 playerPursued = true;
270 }
271 //} else if (loserResult.getFleet().isPlayerFleet() && loserResult.getGoal() != FleetGoal.ESCAPE) {
272 } else if (battle.isPlayerSide(loserResult) && loserResult.getGoal() != FleetGoal.ESCAPE) {
273 playerOnlyRetreated = false;
274 if (winnerResult.getGoal() == FleetGoal.ESCAPE) {
275 playerPursued = true;
276 }
277 }
278
279 DataForEncounterSide winnerData = getDataFor(winnerResult.getFleet());
280 DataForEncounterSide loserData = getDataFor(loserResult.getFleet());
281
282 winnerData.setWonLastEngagement(true);
283 winnerData.setEnemyCanCleanDisengage(winnerResult.enemyCanCleanDisengage());
284 //winnerData.setFleetCanCleanDisengage(loserResult.enemyCanCleanDisengage());
285 loserData.setWonLastEngagement(false);
286 loserData.setEnemyCanCleanDisengage(loserResult.enemyCanCleanDisengage());
287 //loserData.setFleetCanCleanDisengage(winnerResult.enemyCanCleanDisengage());
288
289 winnerData.setDidEnoughToDisengage(true);
290 float damageInFP = 0f;
291 for (FleetMemberAPI member : winnerResult.getDisabled()) {
292 damageInFP += member.getFleetPointCost();
293 }
294 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
295 damageInFP += member.getFleetPointCost();
296 }
297 for (FleetMemberAPI member : winnerResult.getRetreated()) {
298 damageInFP += member.getFleetPointCost();
299 }
300
301// float remaining = 0f;
302// for (FleetMemberAPI member : winnerResult.getFleet().getFleetData().getCombatReadyMembersListCopy()) {
303// remaining += member.getFleetPointCost();
304// }
305// loserData.setDidEnoughToDisengage(damageInFP >= remaining);
306 loserData.setDidEnoughToDisengage(winnerResult.enemyCanCleanDisengage());
307
308
309 winnerData.setLastGoal(winnerResult.getGoal());
310 loserData.setLastGoal(loserResult.getGoal());
311
312 winnerData.getDeployedInLastEngagement().clear();
313 winnerData.getRetreatedFromLastEngagement().clear();
314 winnerData.getInReserveDuringLastEngagement().clear();
315 winnerData.getDisabledInLastEngagement().clear();
316 winnerData.getDestroyedInLastEngagement().clear();
317 winnerData.getDeployedInLastEngagement().addAll(winnerResult.getDeployed());
318 winnerData.getRetreatedFromLastEngagement().addAll(winnerResult.getRetreated());
319 winnerData.getInReserveDuringLastEngagement().addAll(winnerResult.getReserves());
320 winnerData.getDisabledInLastEngagement().addAll(winnerResult.getDisabled());
321 winnerData.getDestroyedInLastEngagement().addAll(winnerResult.getDestroyed());
322
323 loserData.getDeployedInLastEngagement().clear();
324 loserData.getRetreatedFromLastEngagement().clear();
325 loserData.getInReserveDuringLastEngagement().clear();
326 loserData.getDisabledInLastEngagement().clear();
327 loserData.getDestroyedInLastEngagement().clear();
328 loserData.getDeployedInLastEngagement().addAll(loserResult.getDeployed());
329 loserData.getRetreatedFromLastEngagement().addAll(loserResult.getRetreated());
330 loserData.getInReserveDuringLastEngagement().addAll(loserResult.getReserves());
331 loserData.getDisabledInLastEngagement().addAll(loserResult.getDisabled());
332 loserData.getDestroyedInLastEngagement().addAll(loserResult.getDestroyed());
333
334 for (FleetMemberAPI member : loserResult.getDestroyed()) {
335 loserData.addOwn(member, Status.DESTROYED);
336 }
337
338 for (FleetMemberAPI member : loserResult.getDisabled()) {
339 loserData.addOwn(member, Status.DISABLED);
340 }
341
342 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
343 winnerData.addOwn(member, Status.DESTROYED);
344 }
345
346 for (FleetMemberAPI member : winnerResult.getDisabled()) {
347 winnerData.addOwn(member, Status.DISABLED);
348 }
349
350 //if (winnerResult.getFleet().isPlayerFleet()) {
351 if (result.getWinnerResult().getAllEverDeployedCopy() != null) {
352 tallyOfficerTime(winnerData, winnerResult);
353 }
354 //} else if (loserResult.getFleet().isPlayerFleet()) {
355 if (result.getLoserResult().getAllEverDeployedCopy() != null) {
356 tallyOfficerTime(loserData, loserResult);
357 }
358
359 // important, so that in-combat Ship objects can be garbage collected.
360 // Probably some combat engine references in there, too.
361 winnerResult.resetAllEverDeployed();
362 getDataFor(winnerResult.getFleet()).getMemberToDeployedMap().clear();
363 loserResult.resetAllEverDeployed();
364 getDataFor(loserResult.getFleet()).getMemberToDeployedMap().clear();
365
366
367 // moved from applyPostEngagementResult
368 for (FleetMemberAPI member : winnerResult.getDestroyed()) {
369 loserData.addEnemy(member, Status.DESTROYED);
370 }
371 for (FleetMemberAPI member : winnerResult.getDisabled()) {
372 loserData.addEnemy(member, Status.DISABLED);
373 }
374
375 for (FleetMemberAPI member : loserResult.getDestroyed()) {
376 winnerData.addEnemy(member, Status.DESTROYED);
377 }
378 for (FleetMemberAPI member : loserResult.getDisabled()) {
379 winnerData.addEnemy(member, Status.DISABLED);
380 }
381
382
383 FleetGoal winnerGoal = winnerResult.getGoal();
384 FleetGoal loserGoal = loserResult.getGoal();
385 boolean totalWin = loserData.getFleet().getFleetData().getMembersListCopy().isEmpty();
386 boolean playerOut = result.isPlayerOutBeforeEnd();
387
388 if (playerOut) {
389 FleetGoal playerGoal = null;
390 FleetGoal otherGoal = null;
391 if (battle.isPlayerSide(battle.getSideFor(winnerResult.getFleet()))) {
392 playerGoal = winnerGoal;
393 otherGoal = loserGoal;
394 } else {
395 playerGoal = loserGoal;
396 otherGoal = winnerGoal;
397 }
398 if (playerGoal == FleetGoal.ATTACK) {
399 if (otherGoal == FleetGoal.ATTACK) {
400 if (winnerResult.isPlayer()) {
401 lastOutcome = EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN;
402 } else {
403 lastOutcome = EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS;
404 }
405 } else {
406 if (winnerResult.isPlayer()) {
407 lastOutcome = EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN;
408 } else {
409 lastOutcome = EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_LOSS;
410 }
411 }
412 } else {
413 if (winnerResult.isPlayer()) {
414 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN;
415 } else {
416 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_LOSS;
417 }
418 }
419 } else {
420 if (totalWin && winnerData.getFleet().getFleetData().getMembersListCopy().isEmpty()) {
421 lastOutcome = EngagementOutcome.MUTUAL_DESTRUCTION;
422 } else {
423 if (battle.isPlayerSide(battle.getSideFor(winnerResult.getFleet()))) {
424 if (winnerGoal == FleetGoal.ATTACK && loserGoal == FleetGoal.ATTACK) {
425 if (totalWin) {
426 lastOutcome = EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL;
427 } else {
428 lastOutcome = EngagementOutcome.BATTLE_PLAYER_WIN;
429 }
430 } else if (winnerGoal == FleetGoal.ESCAPE) {
431 if (totalWin) {
432 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
433 } else {
434 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_WIN;
435 }
436 } else if (loserGoal == FleetGoal.ESCAPE) {
437 if (totalWin) {
438 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL;
439 } else {
440 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_SUCCESS;
441 }
442 }
443 } else {
444 if (winnerGoal == FleetGoal.ATTACK && loserGoal == FleetGoal.ATTACK) {
445 if (totalWin) {
446 lastOutcome = EngagementOutcome.BATTLE_ENEMY_WIN_TOTAL;
447 } else {
448 lastOutcome = EngagementOutcome.BATTLE_ENEMY_WIN;
449 }
450 } else if (winnerGoal == FleetGoal.ESCAPE) {
451 if (totalWin) {
452 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_WIN_TOTAL;
453 } else {
454 lastOutcome = EngagementOutcome.ESCAPE_ENEMY_WIN;
455 }
456 } else if (loserGoal == FleetGoal.ESCAPE) {
457 if (totalWin) {
458 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_LOSS_TOTAL;
459 } else {
460 lastOutcome = EngagementOutcome.ESCAPE_PLAYER_SUCCESS;
461 }
462 }
463 }
464 }
465 }
466
468 //battle.genCombinedDoNotRemoveEmpty();
470 }
471
472 protected void tallyOfficerTime(DataForEncounterSide data, EngagementResultForFleetAPI result) {
473 float maxTime = 0f;
475 float time = dfm.getShip().getFullTimeDeployed();
476
477 if (time > maxTime) {
478 maxTime = time;
479 }
480
481 time -= dfm.getShip().getTimeDeployedUnderPlayerControl();
482 if (time <= 0) continue;
483
484 PersonAPI person = dfm.getMember().getCaptain();
485 CampaignFleetAPI source = battle.getSourceFleet(dfm.getMember());
486 if (source == null) continue;
487
488 //if (data.getFleet().getFleetData().getOfficerData(person) == null) {
489 if (source.getFleetData().getOfficerData(person) == null) {
490 OfficerEngagementData oed = data.getFleetMemberDeploymentData().get(dfm.getMember());
491 if (oed == null) {
492 oed = new OfficerEngagementData(source);
493 oed.person = null;
494 data.getFleetMemberDeploymentData().put(dfm.getMember(), oed);
495 }
496 time += dfm.getShip().getTimeDeployedUnderPlayerControl();
497 oed.timeDeployed += time;
498 continue; // not an officer
499 }
500
501 OfficerEngagementData oed = data.getOfficerData().get(person);
502 if (oed == null) {
503 oed = new OfficerEngagementData(source);
504 oed.person = person;
505 data.getOfficerData().put(person, oed);
506 }
507 oed.timeDeployed += time;
508 }
509 data.setMaxTimeDeployed(data.getMaxTimeDeployed() + maxTime);
510 }
511
512 public PursueAvailability getPursuitAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet) {
513 DataForEncounterSide otherData = getDataFor(otherFleet);
514
515 if (otherData.isWonLastEngagement()) return PursueAvailability.LOST_LAST_ENGAGEMENT;
516 if (canOutrunOtherFleet(otherFleet, fleet)) return PursueAvailability.TOO_SLOW;
517 if (fleet.getFleetData().getCombatReadyMembersListCopy().isEmpty()) return PursueAvailability.NO_READY_SHIPS;
518 if (otherData.isDidEnoughToDisengage()) return PursueAvailability.TOOK_SERIOUS_LOSSES;
519 return PursueAvailability.AVAILABLE;
520 }
521
522 public DisengageHarryAvailability getDisengageHarryAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet) {
523 DataForEncounterSide otherData = getDataFor(otherFleet);
524 if (otherData.isWonLastEngagement()) return DisengageHarryAvailability.LOST_LAST_ENGAGEMENT;
525 if (fleet.getFleetData().getCombatReadyMembersListCopy().isEmpty()) return DisengageHarryAvailability.NO_READY_SHIPS;
526 //if (otherData.isDidEnoughToDisengage()) return DisengageHarryAvailability.LOST_LAST_ENGAGEMENT
527 return DisengageHarryAvailability.AVAILABLE;
528 }
529
530 public float getDeployCost(FleetMemberAPI member) {
531 return member.getDeployCost();
532 }
533
534 public boolean isLowRepImpact() {
535 boolean lowImpact = getBattle() != null && getBattle().getNonPlayerSide() != null &&
536 getBattle().getPrimary(getBattle().getNonPlayerSide()) != null &&
538 return lowImpact;
539 }
540 public boolean isNoRepImpact() {
541 boolean noImpact = getBattle() != null && getBattle().getNonPlayerSide() != null &&
542 getBattle().getPrimary(getBattle().getNonPlayerSide()) != null &&
544 return noImpact;
545 }
546
547 protected boolean alreadyAdjustedRep = false;
548 public boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText) {
549 return adjustPlayerReputation(dialog, ffText, true, true);
550 }
551 public boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText, boolean okToAdjustAlly, boolean okToAdjustEnemy) {
552 if (alreadyAdjustedRep) return false;
553
555 alreadyAdjustedRep = true;
556
557 boolean printedAdjustmentText = false;
558
559 boolean playerWon = didPlayerWinMostRecentBattleOfEncounter();
560 List<CampaignFleetAPI> playerSide = battle.getPlayerSide();
561 List<CampaignFleetAPI> enemySide = battle.getNonPlayerSide();
562
564 pf.setMoveDestination(pf.getLocation().x, pf.getLocation().y);
565
566 // cases to cover: 1) player destroyed ships 2) player harried/harassed 3) player pursued
567 // i.e. anything other than a non-destructive retreat, w/o an engagement
568 boolean playerWasAggressive = playerDidSeriousDamage || !playerOnlyRetreated;
569 RepActions action = null;
570// if (engagedInHostilities) {
571// action = RepActions.COMBAT_NO_DAMAGE_ESCAPE;
572// }
573 boolean knowsWhoPlayerIs = battle.knowsWhoPlayerIs(enemySide);
574 //boolean lowImpact = battle.getNonPlayerCombined().getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_LOW_REP_IMPACT) == true;
575 boolean lowImpact = isLowRepImpact();
576 if (lowImpact) {
577 for (CampaignFleetAPI enemy : battle.getSnapshotFor(enemySide)) {
578 Misc.makeLowRepImpact(enemy, "battleOnLowImpactSide");
579 }
580 }
581 if (playerPursued && playerWon) {
582 if (knowsWhoPlayerIs && !lowImpact) {
583 action = RepActions.COMBAT_AGGRESSIVE;
584 } else {
585 action = RepActions.COMBAT_AGGRESSIVE_TOFF;
586 }
587 } else if (playerWasAggressive) {
588 if (knowsWhoPlayerIs && !lowImpact) {
589 action = RepActions.COMBAT_NORMAL;
590 } else {
591 action = RepActions.COMBAT_NORMAL_TOFF;
592 }
593 }
594
595 if (isNoRepImpact()) {
596 action = null;
597 }
598
599 if (!okToAdjustEnemy) action = null;
600
601 Set<String> seen = new HashSet<String>();
602 if (action != null) {
603 // use snapshot: ensure loss of reputation with factions if their fleets were destroyed
604 for (CampaignFleetAPI enemy : battle.getSnapshotFor(enemySide)) {
605 String factionId = enemy.getFaction().getId();
606 if (seen.contains(factionId)) continue;
607 seen.add(factionId);
608 Global.getSector().adjustPlayerReputation(new RepActionEnvelope(action, null, dialog.getTextPanel()), factionId);
609 printedAdjustmentText = true;
610 }
611 }
612
613 //if (playerWon) {
614 action = RepActions.COMBAT_HELP_MINOR;
615 float playerFP = 0;
616 float allyFP = 0;
617 float enemyFP = 0;
618 for (CampaignFleetAPI fleet : battle.getSnapshotFor(playerSide)) {
619 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) {
620 if (fleet.isPlayerFleet()) {
621 playerFP += member.getFleetPointCost();
622 } else {
623 allyFP += member.getFleetPointCost();
624 }
625 }
626 }
627 for (CampaignFleetAPI fleet : battle.getSnapshotFor(enemySide)) {
628 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) {
629 enemyFP += member.getFleetPointCost();
630 }
631 }
632 if (allyFP > enemyFP || !playerWon) {
633 action = RepActions.COMBAT_HELP_MINOR;
634 } else if (allyFP < enemyFP * 0.5f) {
635 action = RepActions.COMBAT_HELP_CRITICAL;
636 } else {
637 action = RepActions.COMBAT_HELP_MAJOR;
638 }
639
640// if (playerFPHullDamageToEnemies <= 0) {
641// action = null;
642// } else if (playerFPHullDamageToEnemies < allyFPHullDamageToEnemies * 0.1f) {
643// action = RepActions.COMBAT_HELP_MINOR;
644// }
646 if (f <= 0) {
647 action = null;
648 } else if (f < 0.1f) {
649 action = RepActions.COMBAT_HELP_MINOR;
650 }
651
652 if (action != null) {
654 if (totalDam < 10) {
655 action = RepActions.COMBAT_HELP_MINOR;
656 } else if (totalDam < 20 && action == RepActions.COMBAT_HELP_CRITICAL) {
657 action = RepActions.COMBAT_HELP_MAJOR;
658 }
659 }
660
661 if (battle.isPlayerInvolvedAtStart() && action != null) {
662 //action = RepActions.COMBAT_HELP_MINOR;
663 action = null;
664 }
665
666 if (!okToAdjustAlly) action = null;
667// if (leavingEarly) {
668// action = null;
669// }
670
671 // rep increases
672 seen.clear();
673 for (CampaignFleetAPI ally : battle.getSnapshotFor(playerSide)) {
674 if (ally.isPlayerFleet()) continue;
675
676 String factionId = ally.getFaction().getId();
677 if (seen.contains(factionId)) continue;
678 seen.add(factionId);
679
680 Float friendlyFPHull = playerFPHullDamageToAlliesByFaction.get(ally.getFaction());
681 float threshold = 2f;
682 if (action == RepActions.COMBAT_HELP_MAJOR) {
683 threshold = 5f;
684 } else if (action == RepActions.COMBAT_HELP_CRITICAL) {
685 threshold = 10f;
686 }
687 if (friendlyFPHull != null && friendlyFPHull > threshold) {
688 // can lose reputation with sides that didn't survive
689 //Global.getSector().adjustPlayerReputation(new RepActionEnvelope(RepActions.COMBAT_FRIENDLY_FIRE, (friendlyFPHull - threshold), dialog.getTextPanel()), factionId);
690 } else if (action != null && playerSide.contains(ally)) {
691 // only gain reputation with factions whose fleets actually survived
692 Global.getSector().adjustPlayerReputation(new RepActionEnvelope(action, null, dialog.getTextPanel()), factionId);
693 printedAdjustmentText = true;
694 }
695 }
696
697
698 // friendly fire rep decreases
699 if (okToAdjustAlly) {
700 boolean first = true;
701 seen.clear();
702 for (CampaignFleetAPI ally : battle.getSnapshotFor(playerSide)) {
703 if (ally.isPlayerFleet()) continue;
704
705 String factionId = ally.getFaction().getId();
706 if (Factions.PLAYER.equals(factionId)) continue;
707 if (seen.contains(factionId)) continue;
708 seen.add(factionId);
709
710 Float friendlyFPHull = playerFPHullDamageToAlliesByFaction.get(ally.getFaction());
711 float threshold = 2f;
712 if (action == RepActions.COMBAT_HELP_MAJOR) {
713 threshold = 5f;
714 } else if (action == RepActions.COMBAT_HELP_CRITICAL) {
715 threshold = 10f;
716 }
717 if (friendlyFPHull != null && friendlyFPHull > threshold) {
718 if (first && ffText != null) {
719 first = false;
720 dialog.getTextPanel().addParagraph(ffText);
721 }
722 // can lose reputation with sides that didn't survive
723 Global.getSector().adjustPlayerReputation(new RepActionEnvelope(RepActions.COMBAT_FRIENDLY_FIRE, (friendlyFPHull - threshold), dialog.getTextPanel()), factionId);
724 printedAdjustmentText = true;
725 } else if (action != null && playerSide.contains(ally)) {
726 // only gain reputation with factions whose fleets actually survived
727 //Global.getSector().adjustPlayerReputation(new RepActionEnvelope(action, null, dialog.getTextPanel()), factionId);
728 }
729 }
730 }
731 //}
732 return printedAdjustmentText;
733 }
734
735 return false;
736 }
737
738
743
745 this.textPanelForXPGain = textPanelForXPGain;
746 }
747
748 protected boolean noHarryBecauseOfStation = false;
749 public boolean isNoHarryBecauseOfStation() {
751 }
753 this.noHarryBecauseOfStation = noHarryBecauseOfStation;
754 }
755
759 member.getStatus().resetAmmoState();
760 }
761
762 if (noHarryBecauseOfStation && battle != null) {
763 List<CampaignFleetAPI> otherSide = battle.getNonPlayerSide();
764 CampaignFleetAPI fleet = battle.getPrimary(otherSide);
765 if (fleet.getAI() != null &&
767 fleet.getAI().addAssignmentAtStart(FleetAssignment.STANDING_DOWN, fleet, 0.5f + 0.5f * (float) Math.random(), null);
768 }
769 }
770
771 //Global.getSector().setLastPlayerBattleTimestamp(Global.getSector().getClock().getTimestamp());
773 return;
774 }
775
776 gainXP();
778
779// CampaignFleetAPI winner = getWinnerData().getFleet();
780// CampaignFleetAPI loser = getLoserData().getFleet();
781// List<CampaignFleetAPI> winners = battle.getSideFor(getWinnerData().getFleet());
782// List<CampaignFleetAPI> losers = battle.getSideFor(getLoserData().getFleet());
783 List<CampaignFleetAPI> winners = battle.getSnapshotSideFor(getWinnerData().getFleet());
784 List<CampaignFleetAPI> losers = battle.getSnapshotSideFor(getLoserData().getFleet());
785 if (winners == null || losers == null) return;
786
787 for (CampaignFleetAPI loser : losers) {
788 for (FleetMemberAPI member : loser.getFleetData().getMembersListCopy()) {
789 member.getStatus().resetAmmoState();
790 }
791
792 loser.getVelocity().set(0, 0);
793 if (loser.isPlayerFleet()) continue;
794 if (loser.isPlayerFleet()) loser.setNoEngaging(3f);
795
796 }
797 for (CampaignFleetAPI winner : winners) {
798 for (FleetMemberAPI member : winner.getFleetData().getMembersListCopy()) {
799 member.getStatus().resetAmmoState();
800 }
801
802 winner.getVelocity().set(0, 0);
803 if (winner.isPlayerFleet()) continue;
804 if (winner.isPlayerFleet()) winner.setNoEngaging(3f);
805
806 }
807
808 if (battle.isPlayerSide(winners)) {
809 for (CampaignFleetAPI fleet : battle.getPlayerSide()) {
810 if (fleet.isPlayerFleet()) continue;
811
813 }
814 }
815
817
821 }
822
823 CampaignFleetAPI largestWinner = battle.getPrimary(winners);
824 for (CampaignFleetAPI loser : losers) {
825 if (loser.getFleetData().getMembersListCopy().isEmpty()) {
826 //Global.getSector().reportFleetDewspawned(loser, FleetDespawnReason.DESTROYED_BY_FLEET, winner);
827 loser.despawn(FleetDespawnReason.DESTROYED_BY_BATTLE, battle);
828 }
829 }
830
831 for (CampaignFleetAPI winner : winners) {
832 if (winner.getFleetData().getMembersListCopy().isEmpty()) {
833 //Global.getSector().reportFleetDewspawned(loser, FleetDespawnReason.DESTROYED_BY_FLEET, winner);
834 winner.despawn(FleetDespawnReason.DESTROYED_BY_BATTLE, battle);
835 }
836 }
837
838 for (CampaignFleetAPI enemy : battle.getBothSides()) {
839 if (enemy.getAI() instanceof ModularFleetAIAPI) {
840 ModularFleetAIAPI mAI = (ModularFleetAIAPI) enemy.getAI();
842 }
843 }
844
845 }
846
847
848
849
851 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
852 EngagementResultForFleetAPI loserResult = result.getLoserResult();
853 return performPostVictoryRecovery(winnerResult, loserResult);
854 }
856 EngagementResultForFleetAPI winnerResult = result.getWinnerResult();
857 EngagementResultForFleetAPI loserResult = result.getLoserResult();
858 float f = performPostVictoryRecovery(winnerResult, loserResult);
859 f += performPostVictoryRecovery(loserResult, winnerResult);
860 f /= 2f;
861 return f;
862 }
863
864
866 DataForEncounterSide winnerData = getDataFor(winnerResult.getFleet());
867 DataForEncounterSide loserData = getDataFor(loserResult.getFleet());
868
869 //float totalFpUsed = 0f;
870 float loserDepDestroyed = 0f;
871 float loserDepLeft = 0f;
872
873 for (FleetMemberAPI member : loserData.getRetreatedFromLastEngagement()) {
874 loserDepLeft += member.getDeploymentPointsCost();
875 }
876 for (FleetMemberAPI member : loserData.getInReserveDuringLastEngagement()) {
877 loserDepLeft += member.getDeploymentPointsCost();
878 }
879
880 for (FleetMemberAPI member : loserData.getDestroyedInLastEngagement()) {
881 loserDepDestroyed += member.getDeploymentPointsCost();
882 }
883 for (FleetMemberAPI member : loserData.getDisabledInLastEngagement()) {
884 loserDepDestroyed += member.getDeploymentPointsCost();
885 }
886 for (FleetMemberAPI member : loserData.getRetreatedFromLastEngagement()) {
887 if (member.isFighterWing()) {
888 DeployedFleetMemberAPI dfm = getDataFor(loserData.getFleet()).getMemberToDeployedMap().get(member);
889 if (dfm != null && dfm.getMember() == member) {
890 float deploymentCR = dfm.getShip().getWingCRAtDeployment();
891 float finalCR = deploymentCR;
892 //float finalCR = dfm.getShip().getRemainingWingCR();
893 if (deploymentCR > finalCR) {
895 float extraCraftLost = (deploymentCR - finalCR) / crPer;
896 float wingSize = dfm.getMember().getNumFightersInWing();
897 if (extraCraftLost >= 1) {
898 loserDepDestroyed += Math.min(1f, extraCraftLost / wingSize) * member.getDeploymentPointsCost();
899 }
900 }
901 }
902 }
903 }
904
905 float totalRecovery = 0f;
906 float count = 0f;
907 for (FleetMemberAPI member : winnerData.getDeployedInLastEngagement()) {
908 float dp = member.getDeploymentPointsCost();
909 float recoveryFraction = Math.max(0, (dp * 1.25f - loserDepDestroyed)) / dp;
910// if (member.getFleetData() != null && member.getFleetData().getFleet() != null &&
911// member.getFleetData().getFleet().isPlayerFleet()) {
912 if (loserDepDestroyed > loserDepLeft * 2f) {
913 recoveryFraction = Math.max(0, (dp * 0.75f - loserDepDestroyed)) / dp;
914 }
915 if (recoveryFraction > 1f) recoveryFraction = 1f;
916 if (loserDepDestroyed <= 0) recoveryFraction = 1f;
917
918 float deployCost = getDeployCost(member);
919 if (preEngagementCRForWinner.containsKey(member)) {
920 float prevCR = preEngagementCRForWinner.get(member);
921 if (prevCR < deployCost) {
922 prevCR = deployCost;
923 }
924 }
925
926 float recoveryAmount = Math.round(deployCost * recoveryFraction * 100f) / 100f;
927 if (member.getHullSpec().hasTag(Tags.FULL_CR_RECOVERY)) {
928 recoveryAmount = member.getRepairTracker().getMaxCR() - member.getRepairTracker().getCR();
929 }
930
931 totalRecovery += recoveryAmount;
932 count++;
933
934 if (recoveryAmount <= 0) continue;
935
936
937 member.getRepairTracker().applyCREvent(recoveryAmount, "Post engagement recovery");
938 }
939
940 if (count <= 0) return 0;
941
942 return Math.round(totalRecovery / count * 100f) / 100f;
943 }
944
945
946
947 public void applyPursuitOption(CampaignFleetAPI pursuingFleet, CampaignFleetAPI otherFleet, PursuitOption pursuitOption) {
948
949 if (Misc.isPlayerOrCombinedPlayerPrimary(pursuingFleet) && pursuitOption != PursuitOption.LET_THEM_GO) {
950 playerOnlyRetreated = false;
951 }
952
953 DataForEncounterSide pursuer = getDataFor(pursuingFleet);
954 DataForEncounterSide other = getDataFor(otherFleet);
955
956 if (pursuitOption == PursuitOption.HARRY) {
957 for (FleetMemberAPI member : otherFleet.getFleetData().getMembersListCopy()) {
958 float deployCost = getDeployCost(member);
959
960 float harryCost = deployCost * 1f;
961 member.getRepairTracker().applyCREvent(-harryCost, "harried while disengaging");
962 }
963 }
964 }
965
966
967
968 protected EngagementOutcome lastOutcome = null;
969 public EngagementOutcome getLastEngagementOutcome() {
970 return lastOutcome;
971 }
972
973 public boolean isBattleOver() {
974 if (hasWinnerAndLoser()) return true;
975
976 return lastOutcome != null &&
977 lastOutcome != EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN &&
978 lastOutcome != EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS &&
979 lastOutcome != EngagementOutcome.BATTLE_ENEMY_WIN &&
980 lastOutcome != EngagementOutcome.BATTLE_PLAYER_WIN;
981 }
982
983 public boolean wasLastEngagementEscape() {
984 return lastOutcome != null &&
985// lastOutcome != EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN &&
986// lastOutcome != EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_LOSS &
987 lastOutcome != EngagementOutcome.BATTLE_ENEMY_WIN &&
988 lastOutcome != EngagementOutcome.BATTLE_PLAYER_WIN;
989 }
990
991 public boolean didPlayerWinLastEngagement() {
992 return lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN ||
993 lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL ||
994 lastOutcome == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN ||
995 lastOutcome == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN ||
996 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN ||
997 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL ||
998 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_SUCCESS ||
999 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN ||
1000 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
1001 }
1002
1009 if (getDataFor(Global.getSector().getPlayerFleet()).disengaged()) return false;
1010
1011 return didPlayerWinEncounterOutright() || lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN;
1012 }
1013
1019 if (getDataFor(Global.getSector().getPlayerFleet()).disengaged()) return false;
1020
1021 // non-fighting "win", i.e. harrying a weaker enemy
1022 //if (lastOutcome == null && getWinner() == Global.getSector().getPlayerFleet()) {
1023 //if (lastOutcome == null && battle.isPlayerSide(battle.getSideFor(getWinner()))) {
1025 return true;
1026 }
1027
1028 return lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL ||
1029 //lastOutcome == EngagementOutcome.BATTLE_PLAYER_WIN ||
1030 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_LOSS_TOTAL ||
1031 lastOutcome == EngagementOutcome.ESCAPE_ENEMY_SUCCESS ||
1032 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN ||
1033 lastOutcome == EngagementOutcome.ESCAPE_PLAYER_WIN_TOTAL;
1034 }
1035
1036
1037 public int getCreditsLooted() {
1038 return creditsLooted;
1039 }
1040
1041 public float getSalvageMult(Status status) {
1042 float mult = 1f;
1043 switch (status) {
1044 case DESTROYED:
1045 //mult = 0.5f;
1046 mult = 1f;
1047 break;
1048 case DISABLED:
1049 mult = 1f;
1050 break;
1051 case REPAIRED:
1052 mult = 1f;
1053 break;
1054 case CAPTURED:
1055 mult = 0.1f;
1056 break;
1057 }
1058 return mult;
1059 }
1060
1061 public float getCargoLootMult(Status status) {
1062 float mult = 1f;
1063 switch (status) {
1064 case DESTROYED:
1065 mult = 1f;
1066 break;
1067 case DISABLED:
1068 mult = 1f;
1069 break;
1070 case REPAIRED:
1071 mult = 1f;
1072 break;
1073 case CAPTURED:
1074 mult = 1f;
1075 break;
1076 }
1077 return mult;
1078 }
1079
1080// public List<FleetMemberAPI> repairShips() {
1081// DataForEncounterSide winner = getWinnerData();
1082// return repairShips(winner);
1083//
1086// }
1087
1088 public static enum EngageBoardableOutcome {
1089 ESCAPED,
1090 DISABLED,
1091 DESTROYED,
1092 }
1093
1094 public EngageBoardableOutcome engageBoardableShip(FleetMemberAPI toBoard,
1095 CampaignFleetAPI fleetItBelongsTo,
1096 CampaignFleetAPI attackingFleet) {
1097 float r = (float) Math.random();
1098// if (r < ENGAGE_ESCAPE_CHANCE && !Misc.isPlayerOrCombinedContainingPlayer(attackingFleet)) {
1099// // escaped
1100// CampaignFleetAPI fleet = getBattle().getSourceFleet(toBoard);
1101// letBoardableGo(toBoard, fleet, attackingFleet);
1102//
1103// return EngageBoardableOutcome.ESCAPED;
1104// } else
1106 // disabled
1107 DataForEncounterSide attackerSide = getDataFor(attackingFleet);
1108 attackerSide.changeEnemy(toBoard, Status.DISABLED);
1109 toBoard.getStatus().disable();
1110 return EngageBoardableOutcome.DISABLED;
1111 } else {
1112 DataForEncounterSide attackerSide = getDataFor(attackingFleet);
1113 attackerSide.changeEnemy(toBoard, Status.DESTROYED);
1114 toBoard.getStatus().disable();
1115 return EngageBoardableOutcome.DESTROYED;
1116 }
1117 }
1118
1119
1120 public static enum BoardingAttackType {
1121 SHIP_TO_SHIP,
1122 LAUNCH_FROM_DISTANCE,
1123 }
1124 public static enum BoardingOutcome {
1125 SUCCESS,
1126 SELF_DESTRUCT,
1127 SUCCESS_TOO_DAMAGED,
1128 SHIP_ESCAPED,
1129 SHIP_ESCAPED_CLEAN,
1130 }
1131
1132 public static class BoardingResult {
1133 private BoardingOutcome outcome;
1134 private CrewCompositionAPI attackerLosses = Global.getFactory().createCrewComposition();
1135 private CrewCompositionAPI defenderLosses = Global.getFactory().createCrewComposition();
1136 private FleetMemberAPI member;
1137 private List<FleetMemberAPI> lostInSelfDestruct = new ArrayList<FleetMemberAPI>();
1138
1139 public BoardingOutcome getOutcome() {
1140 return outcome;
1141 }
1142 public List<FleetMemberAPI> getLostInSelfDestruct() {
1143 return lostInSelfDestruct;
1144 }
1145 public void setOutcome(BoardingOutcome outcome) {
1146 this.outcome = outcome;
1147 }
1148 public CrewCompositionAPI getAttackerLosses() {
1149 return attackerLosses;
1150 }
1151 public void setAttackerLosses(CrewCompositionAPI attackerLosses) {
1152 this.attackerLosses = attackerLosses;
1153 }
1154 public CrewCompositionAPI getDefenderLosses() {
1155 return defenderLosses;
1156 }
1157 public void setDefenderLosses(CrewCompositionAPI defenderLosses) {
1158 this.defenderLosses = defenderLosses;
1159 }
1160 public FleetMemberAPI getMember() {
1161 return member;
1162 }
1163 public void setMember(FleetMemberAPI member) {
1164 this.member = member;
1165 }
1166
1167 }
1168
1169 public static final float SELF_DESTRUCT_CHANCE = 0.25f;
1170 public static final float CIV_SELF_DESTRUCT_CHANCE = 0.05f;
1171
1172 public static final float ENGAGE_ESCAPE_CHANCE = 0.25f;
1173 public static final float ENGAGE_DISABLE_CHANCE = 0.5f;
1174 public static final float ENGAGE_DESTROY_CHANCE = 0.25f;
1175
1176 public static final float LAUNCH_CLEAN_ESCAPE_CHANCE = 0.5f;
1177 public static final float DOCK_SUCCESS_CHANCE = 0.5f;
1178 public static final float LAUNCH_SUCCESS_CHANCE = 0.25f;
1179
1180 //public static final float DEFENDER_BONUS = 4f;
1181 //public static final float DEFENDER_VS_LAUNCH_BONUS = 3f;
1182
1183 public BoardingResult boardShip(FleetMemberAPI member, CampaignFleetAPI attacker, CampaignFleetAPI defender) {
1184
1185
1186 DataForEncounterSide attackerSide = getDataFor(attacker);
1187 DataForEncounterSide defenderSide = getDataFor(defender);
1188
1189 float attackerMarineMult = attacker.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1190 float defenderMarineMult = defender.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1191
1192 float crewMult = 2f;
1193 float marineMult = 7f;
1194
1195
1196 float attackerStr = attacker.getCargo().getMarines() * marineMult;
1197 attackerStr *= attackerMarineMult;
1198
1199 CrewCompositionAPI defenderCrew = member.getCrewComposition();
1200 float defenderStr = defenderCrew.getCrew() * crewMult + defenderCrew.getMarines() * marineMult;
1201 defenderStr *= defenderMarineMult;
1202
1203 //defenderStr *= Global.getSettings().getFloat("boardingDifficulty");
1204
1205 Random rand = new Random(1300000 * (member.getId().hashCode() + defender.getId().hashCode() + Global.getSector().getClock().getDay()));
1206 attackerStr *= 0.75f + 0.25f * rand.nextFloat();
1207 defenderStr *= 0.75f + 0.25f * rand.nextFloat();
1208
1209 boolean attackerWin = attackerStr > defenderStr;
1210 boolean defenderWin = !attackerWin;
1211
1212 BoardingResult result = new BoardingResult();
1213 result.setMember(member);
1214
1215
1216 BoardingOutcome outcome = BoardingOutcome.SUCCESS;
1217 if (defenderWin) {
1218 outcome = BoardingOutcome.SHIP_ESCAPED;
1219 }
1220
1222 boardingParty.addMarines(attacker.getCargo().getMarines());
1223
1224 result.setOutcome(outcome);
1225 switch (outcome) {
1226 case SHIP_ESCAPED:
1227 computeCrewLossFromBoarding(result, member, boardingParty, attackerStr, defenderStr);
1228 result.getAttackerLosses().removeFromCargo(attacker.getCargo());
1229 member.getCrewComposition().removeAll(result.getDefenderLosses());
1230
1231 letBoardableGo(member, defender, attacker);
1232 break;
1233 case SUCCESS:
1234 computeCrewLossFromBoarding(result, member, boardingParty, attackerStr, defenderStr);
1235 result.getAttackerLosses().removeFromCargo(attacker.getCargo());
1236 member.getCrewComposition().removeAll(result.getDefenderLosses());
1237
1238 //attackerSide.removeEnemyCasualty(member);
1239 attacker.getFleetData().addFleetMember(member);
1241
1242 member.getRepairTracker().setMothballed(true);
1243 //defender.getFleetData().removeFleetMember(member);
1244
1245 attackerSide.changeEnemy(member, Status.CAPTURED);
1246 defenderSide.changeOwn(member, Status.CAPTURED);
1247
1248 attackerSide.getInReserveDuringLastEngagement().add(member);
1249 defenderSide.getDestroyedInLastEngagement().remove(member);
1250 defenderSide.getDisabledInLastEngagement().remove(member);
1251
1252 member.setOwner(0);
1254 break;
1255 }
1256
1257 return result;
1258 }
1259
1261 DataForEncounterSide attackerSide = getDataFor(attacker);
1262 DataForEncounterSide defenderSide = getDataFor(defender);
1263 float attackerMarineMult = attacker.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1264 float defenderMarineMult = defender.getCommanderStats().getMarineEffectivnessMult().getModifiedValue();
1265
1266 float crewMult = 2f;
1267 float marineMult = 7f;
1268
1269 Random rand = new Random();
1270
1271 float wins = 0;
1272 float losses = 0;
1273
1274 for (int i = 0; i < 100; i++) {
1275 float attackerStr = attacker.getCargo().getMarines() * marineMult;
1276 attackerStr *= attackerMarineMult;
1277
1278 CrewCompositionAPI defenderCrew = member.getCrewComposition();
1279 float defenderStr = defenderCrew.getCrew() * crewMult + defenderCrew.getMarines() * marineMult;
1280 defenderStr *= defenderMarineMult;
1281
1282 //defenderStr *= Global.getSettings().getFloat("boardingDifficulty");
1283
1284 attackerStr *= 0.75f + 0.25f * rand.nextFloat();
1285 defenderStr *= 0.75f + 0.25f * rand.nextFloat();
1286
1287 boolean attackerWin = attackerStr > defenderStr;
1288 if (attackerWin) wins++;
1289 else losses++;
1290 }
1291
1292 return wins;
1293 }
1294
1295
1296 protected void computeMissedLaunchLosses(BoardingResult result, CrewCompositionAPI boardingParty) {
1297 result.getAttackerLosses().addAll(boardingParty);
1298 result.getAttackerLosses().multiplyBy((float) Math.random() * 0.2f);
1299 }
1300
1301 protected void computeCrewLossFromBoarding(BoardingResult result,
1302 FleetMemberAPI member, CrewCompositionAPI boardingParty,
1303 float attackerStr, float defenderStr) {
1304
1305 if (attackerStr < 1) attackerStr = 1;
1306 if (defenderStr < 1) defenderStr = 1;
1307 float cap = 2f;
1308 float attackerExtraStr = 0f;
1309 if (attackerStr > defenderStr * cap) {
1310 attackerExtraStr = attackerStr - defenderStr * cap;
1311 attackerStr = defenderStr * cap;
1312 }
1313 if (defenderStr > attackerStr * cap) {
1314 defenderStr = attackerStr * cap;
1315 }
1316
1317 float attackerLosses = defenderStr / (attackerStr + defenderStr);
1318 float defenderLosses = attackerStr / (attackerStr + defenderStr);
1319
1320 if (attackerStr > defenderStr) {
1321 result.getAttackerLosses().addAll(boardingParty);
1322 result.getAttackerLosses().multiplyBy(attackerLosses * attackerStr / (attackerExtraStr + attackerStr));
1323 result.getDefenderLosses().addAll(member.getCrewComposition());
1324 result.getDefenderLosses().multiplyBy(defenderLosses);
1325 } else {
1326 result.getAttackerLosses().addAll(boardingParty);
1327 result.getAttackerLosses().multiplyBy(attackerLosses);
1328 result.getDefenderLosses().addAll(member.getCrewComposition());
1329 result.getDefenderLosses().multiplyBy(defenderLosses);
1330 }
1331 //member.getCrewComposition().removeAll(result.getDefenderLosses());
1332 }
1333
1334
1336 CrewCompositionAPI boardingParty, BoardingAttackType attackType,
1337 List<FleetMemberAPI> boardingTaskForce,
1338 CampaignFleetAPI attacker, CampaignFleetAPI defender,
1339 BoardingResult result) {
1340
1341 DataForEncounterSide attackerSide = getDataFor(attacker);
1342 DataForEncounterSide defenderSide = getDataFor(defender);
1343
1344 attackerSide.changeEnemy(member, Status.DESTROYED);
1345 defenderSide.changeOwn(member, Status.DESTROYED);
1346
1347
1349
1350 if (attackType == BoardingAttackType.SHIP_TO_SHIP) {
1351 for (FleetMemberAPI fm : boardingTaskForce) {
1352 float damage = member.getStats().getFluxCapacity().getModifiedValue() * (1f + (float) Math.random() * 0.5f);
1353 float hull = fm.getStatus().getHullFraction();
1354 float hullDamageFactor = 0f;
1355 fm.getStatus().applyDamage(damage);
1356 if (fm.getStatus().getHullFraction() <= 0) {
1357 fm.getStatus().disable();
1358 attacker.getFleetData().removeFleetMember(fm);
1359 attackerSide.addOwn(fm, Status.DESTROYED);
1360 //total.addAll(fm.getCrewComposition());
1361
1362 attackerSide.getRetreatedFromLastEngagement().remove(fm);
1363 attackerSide.getInReserveDuringLastEngagement().remove(fm);
1364 attackerSide.getDeployedInLastEngagement().remove(fm);
1365 attackerSide.getDestroyedInLastEngagement().add(fm);
1366
1367 result.getLostInSelfDestruct().add(fm);
1368
1369 hullDamageFactor = 1f;
1370 } else {
1371 float newHull = fm.getStatus().getHullFraction();
1372 float diff = hull - newHull;
1373 if (diff < 0) diff = 0;
1374 hullDamageFactor = diff;
1375 }
1377 temp.addAll(fm.getCrewComposition());
1378 float lossFraction = computeLossFraction(fm, null, fm.getStatus().getHullFraction(), hullDamageFactor);
1379 temp.multiplyBy(lossFraction);
1380 total.addAll(temp);
1381 }
1382 //total.removeAll(boardingParty);
1383 }
1384
1385 float lossFraction = computeLossFraction(null, null, 0f, 1f);
1386 total.setMarines(Math.round(Math.max(total.getMarines(), boardingParty.getMarines() * lossFraction)));
1387 total.setCrew(Math.round(Math.max(total.getCrew(), boardingParty.getCrew() * lossFraction)));
1388
1389 //result.getAttackerLosses().addAll(boardingParty);
1390// float lossFraction = computeLossFraction(boardingTaskForce.get(0), 0f, 1f);
1391// total.multiplyBy(lossFraction);
1392
1393 result.getAttackerLosses().addAll(total);
1394 result.getDefenderLosses().addAll(member.getCrewComposition());
1395 }
1396
1397 public void letBoardableGo(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet) {
1398 DataForEncounterSide attackerSide = getDataFor(attackingFleet);
1399 attackerSide.removeEnemyCasualty(toBoard);
1400
1401 DataForEncounterSide defenderSide = getDataFor(fleetItBelongsTo);
1402 defenderSide.removeOwnCasualty(toBoard);
1403
1404
1405 defenderSide.getDestroyedInLastEngagement().remove(toBoard);
1406 defenderSide.getDisabledInLastEngagement().remove(toBoard);
1407 defenderSide.getRetreatedFromLastEngagement().add(toBoard);
1408
1409 if (!fleetItBelongsTo.isValidPlayerFleet()) {
1410 fleetItBelongsTo.getCargo().removeCrew(fleetItBelongsTo.getCargo().getCrew());
1411 fleetItBelongsTo.getCargo().removeMarines(fleetItBelongsTo.getCargo().getMarines());
1412 }
1413
1414 FleetDataAPI data = fleetItBelongsTo.getFleetData();
1415 data.addFleetMember(toBoard);
1416
1417 getBattle().getCombinedFor(fleetItBelongsTo).getFleetData().addFleetMember(toBoard);
1418
1419 toBoard.getCrewComposition().addToCargo(fleetItBelongsTo.getCargo());
1420 }
1421
1422 public List<FleetMemberAPI> getStoryRecoverableShips() {
1423 return storyRecoverableShips;
1424 }
1425
1426 protected List<FleetMemberAPI> recoverableShips = new ArrayList<FleetMemberAPI>();
1427 protected List<FleetMemberAPI> storyRecoverableShips = new ArrayList<FleetMemberAPI>();
1428 public List<FleetMemberAPI> getRecoverableShips(BattleAPI battle, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet) {
1429
1430 storyRecoverableShips.clear();
1431
1432 List<FleetMemberAPI> result = new ArrayList<FleetMemberAPI>();
1433// int max = Global.getSettings().getMaxShipsInFleet() -
1434// Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy().size();
1435// if (Misc.isPlayerOrCombinedContainingPlayer(winningFleet) && max <= 0) {
1436// return result;
1437// }
1438
1439 if (Misc.isPlayerOrCombinedContainingPlayer(otherFleet)) {
1440 return result;
1441 }
1442
1443 DataForEncounterSide winnerData = getDataFor(winningFleet);
1444 DataForEncounterSide loserData = getDataFor(otherFleet);
1445
1446 float playerContribMult = computePlayerContribFraction();
1447 List<FleetMemberData> enemyCasualties = winnerData.getEnemyCasualties();
1448 List<FleetMemberData> ownCasualties = winnerData.getOwnCasualties();
1449 List<FleetMemberData> all = new ArrayList<FleetMemberData>();
1450 all.addAll(ownCasualties);
1451 Collections.sort(all, new Comparator<FleetMemberData>() {
1452 public int compare(FleetMemberData o1, FleetMemberData o2) {
1453 int result = o2.getMember().getVariant().getSMods().size() - o1.getMember().getVariant().getSMods().size();
1454 if (result == 0) {
1455 result = o2.getMember().getHullSpec().getHullSize().ordinal() - o1.getMember().getHullSpec().getHullSize().ordinal();
1456 }
1457 return result;
1458 }
1459 });
1460
1461
1462 //Random random = Misc.getRandom(battle.getSeed(), 11);
1463 Random random = Misc.getRandom(Global.getSector().getPlayerBattleSeed(), 11);
1464 //System.out.println("BATTLE SEED: " + Global.getSector().getPlayerBattleSeed());
1465
1466 // since the number of recoverable ships is limited, prefer "better" ships
1468
1469 // doesn't matter how it's sorted, as long as it's consistent so that
1470 // the order it's insertied into the picker in is the same
1471 List<FleetMemberData> enemy = new ArrayList<FleetMemberData>(enemyCasualties);
1472 Collections.sort(enemy, new Comparator<FleetMemberData>() {
1473 public int compare(FleetMemberData o1, FleetMemberData o2) {
1474 int result = o2.getMember().getId().hashCode() - o1.getMember().getId().hashCode();
1475 return result;
1476 }
1477 });
1478
1479 for (FleetMemberData curr : enemy) {
1480 float base = 10f;
1481 switch (curr.getMember().getHullSpec().getHullSize()) {
1482 case CAPITAL_SHIP: base = 40f; break;
1483 case CRUISER: base = 20f; break;
1484 case DESTROYER: base = 10f; break;
1485 case FRIGATE: base = 5f; break;
1486 }
1487 float w = curr.getMember().getUnmodifiedDeploymentPointsCost() / base;
1488
1489 enemyPicker.add(curr, w);
1490 }
1491 List<FleetMemberData> sortedEnemy = new ArrayList<FleetMemberData>();
1492 while (!enemyPicker.isEmpty()) {
1493 sortedEnemy.add(enemyPicker.pickAndRemove());
1494 }
1495
1496
1497 all.addAll(sortedEnemy);
1498
1499// for (FleetMemberData curr : all) {
1500// System.out.println(curr.getMember().getHullId());
1501// }
1502
1504
1505 int maxRecoverablePerType = 24;
1506
1507 float probLessDModsOnNext = Global.getSettings().getFloat("baseProbLessDModsOnRecoverableEnemyShip");
1508 float lessDmodsOnNextMult = Global.getSettings().getFloat("lessDModsOnRecoverableEnemyShipMultNext");
1509
1510 int count = 0;
1511 for (FleetMemberData data : all) {
1512// if (data.getMember().getHullId().contains("legion")) {
1513// System.out.println("wefwefwefe");
1514// }
1515 //if (data.getMember().getHullSpec().getHints().contains(ShipTypeHints.UNBOARDABLE)) continue;
1516 if (Misc.isUnboardable(data.getMember())) continue;
1517 if (data.getStatus() != Status.DISABLED && data.getStatus() != Status.DESTROYED) continue;
1518
1519 boolean own = ownCasualties.contains(data);
1520 if (own && data.getMember().isAlly()) continue;
1521
1522// if (data.getMember().getHullId().startsWith("vanguard_pirates")) {
1523// System.out.println("wefwefwefe12341234");
1524// }
1525
1526 float mult = 1f;
1527 if (data.getStatus() == Status.DESTROYED) mult = 0.5f;
1528 if (!own) mult *= playerContribMult;
1529
1530
1531 boolean useOfficerRecovery = false;
1532 if (own) {
1533 useOfficerRecovery = winnerData.getMembersWithOfficerOrPlayerAsOrigCaptain().contains(data.getMember());
1534 if (useOfficerRecovery) {
1535 mult = 1f;
1536 }
1537 }
1538
1539 boolean noRecovery = false;
1540 if (battle != null &&
1541 battle.getSourceFleet(data.getMember()) != null) {
1542 CampaignFleetAPI fleet = battle.getSourceFleet(data.getMember());
1544 noRecovery = true;
1545 }
1546 }
1547
1548// if (data.getMember().getHullId().startsWith("cerberus")) {
1549// System.out.println("wefwefew");
1550// }
1551 boolean normalRecovery = !noRecovery &&
1552 Misc.isShipRecoverable(data.getMember(), playerFleet, own, useOfficerRecovery, 1f * mult);
1553 boolean storyRecovery = !noRecovery && !normalRecovery;
1554
1555 boolean alwaysRec = data.getMember().getVariant().hasTag(Tags.VARIANT_ALWAYS_RECOVERABLE);
1556
1557 float shipRecProb = data.getMember().getStats().getDynamic().getMod(Stats.INDIVIDUAL_SHIP_RECOVERY_MOD).computeEffective(0f);
1558 if (!own && !alwaysRec && (storyRecovery || normalRecovery) && shipRecProb < 1f) {
1559 float per = Global.getSettings().getFloat("probNonOwnNonRecoverablePerDMod");
1560 float perAlready = Global.getSettings().getFloat("probNonOwnNonRecoverablePerAlreadyRecoverable");
1561 float max = Global.getSettings().getFloat("probNonOwnNonRecoverableMax");
1562 int dmods = DModManager.getNumDMods(data.getMember().getVariant());
1563
1564 float assumedAddedDmods = 3f;
1565 assumedAddedDmods -= Global.getSector().getPlayerFleet().getStats().getDynamic().getValue(Stats.SHIP_DMOD_REDUCTION, 0) * 0.5f;
1566 assumedAddedDmods = Math.min(assumedAddedDmods, 5 - dmods);
1567
1568 float recoveredSoFar = 0f;
1569 if (storyRecovery) recoveredSoFar = storyRecoverableShips.size();
1570 else recoveredSoFar = result.size();
1571
1572 if (random.nextFloat() < Math.min(max, (dmods + assumedAddedDmods) * per) + recoveredSoFar * perAlready) {
1573 noRecovery = true;
1574 }
1575 }
1576
1577
1578 //if (true || Misc.isShipRecoverable(data.getMember(), playerFleet, own, useOfficerRecovery, battle.getSeed(), 1f * mult)) {
1579 if (!noRecovery && (normalRecovery || storyRecovery)) {
1580 //if (Misc.isShipRecoverable(data.getMember(), playerFleet, battle.getSeed(), 1f * mult)) {
1581
1582 if (!own || !Misc.isUnremovable(data.getMember().getCaptain())) {
1583 String aiCoreId = null;
1584 if (own && data.getMember().getCaptain() != null &&
1585 data.getMember().getCaptain().isAICore()) {
1586 aiCoreId = data.getMember().getCaptain().getAICoreId();
1587 }
1588
1589 // if it's an AI core on a player ship, then:
1590 // 1. It's integrated/unremovable, so, don't remove (we don't even end up here)
1591 // 2. Ship will be recovered and will still have it, or
1592 // 3. Ship will not be recovered, and it will get added to loot in lootWeapons()
1593 boolean keepCaptain = false;
1594 // don't do this - want to only show the AI core in recovery dialog when
1595 // it's integrated and would be lost if not recovered
1596// if (own && (data.getMember().getCaptain() == null ||
1597// data.getMember().getCaptain().isAICore())) {
1598// keepCaptain = true;
1599// }
1600 if (!keepCaptain) {
1601 if (aiCoreId != null) {
1602 data.getMember().setCaptain(Global.getFactory().createPerson());
1603 data.getMember().getCaptain().getMemoryWithoutUpdate().set(
1604 "$aiCoreIdForRecovery", aiCoreId);
1605 } else if (!own && data.getMember().getCaptain() != null &&
1606 data.getMember().getCaptain().isAICore()) {
1607 aiCoreId = data.getMember().getCaptain().getAICoreId();
1608 data.getMember().setCaptain(Global.getFactory().createPerson());
1609 data.getMember().getCaptain().getMemoryWithoutUpdate().set(
1610 "$aiCoreIdForPossibleRecovery", aiCoreId);
1611
1612 }
1613 }
1614 }
1615
1616 ShipVariantAPI variant = data.getMember().getVariant();
1617 variant = variant.clone();
1618 variant.setSource(VariantSource.REFIT);
1619
1620 // maybe this was necessary? commenting this out to for simulator to be able to unlock recoverable ship variants
1621 //variant.setOriginalVariant(null);
1622
1623 //DModManager.setDHull(variant);
1624 data.getMember().setVariant(variant, false, true);
1625
1626 boolean lessDmods = false;
1627 if (!own && data.getStatus() != Status.DESTROYED && random.nextFloat() < probLessDModsOnNext) {
1628 lessDmods = true;
1629 probLessDModsOnNext *= lessDmodsOnNextMult;
1630 }
1631
1632 //Random dModRandom = new Random(1000000 * (data.getMember().getId().hashCode() + Global.getSector().getClock().getDay()));
1633 Random dModRandom = new Random(1000000 * data.getMember().getId().hashCode() + Global.getSector().getPlayerBattleSeed());
1634 dModRandom = Misc.getRandom(dModRandom.nextLong(), 5);
1635 if (lessDmods) {
1636 DModManager.reduceNextDmodsBy = 3;
1637 }
1638
1639 float probAvoidDmods =
1640 data.getMember().getStats().getDynamic().getMod(
1641 Stats.DMOD_AVOID_PROB_MOD).computeEffective(0f);
1642
1643 float probAcquireDmods =
1644 data.getMember().getStats().getDynamic().getMod(
1645 Stats.DMOD_ACQUIRE_PROB_MOD).computeEffective(1f);
1646
1647 if (dModRandom.nextFloat() >= probAvoidDmods && dModRandom.nextFloat() < probAcquireDmods) {
1648 DModManager.addDMods(data, own, Global.getSector().getPlayerFleet(), dModRandom);
1649 if (DModManager.getNumDMods(variant) > 0) {
1650 DModManager.setDHull(variant);
1651 }
1652 }
1653
1654 float weaponProb = Global.getSettings().getFloat("salvageWeaponProb");
1655 float wingProb = Global.getSettings().getFloat("salvageWingProb");
1656 if (own) {
1657 weaponProb = Global.getSettings().getFloat("salvageOwnWeaponProb");
1658 wingProb = Global.getSettings().getFloat("salvageOwnWingProb");
1659 weaponProb = playerFleet.getStats().getDynamic().getValue(Stats.OWN_WEAPON_RECOVERY_MOD, weaponProb);
1660 wingProb = playerFleet.getStats().getDynamic().getValue(Stats.OWN_WING_RECOVERY_MOD, wingProb);
1661 }
1662
1663 boolean retain = data.getMember().getHullSpec().hasTag(Tags.TAG_RETAIN_SMODS_ON_RECOVERY) ||
1664 data.getMember().getVariant().hasTag(Tags.TAG_RETAIN_SMODS_ON_RECOVERY);
1665 prepareShipForRecovery(data.getMember(), own, true, !own && !retain, weaponProb, wingProb, salvageRandom);
1666
1667 if (normalRecovery) {
1668 if (result.size() < maxRecoverablePerType) {
1669 result.add(data.getMember());
1670 }
1671 } else if (storyRecovery) {
1672 if (storyRecoverableShips.size() < maxRecoverablePerType) {
1673 storyRecoverableShips.add(data.getMember());
1674 }
1675 }
1676
1677// count++;
1678// if (count >= max) break;
1679 }
1680
1681
1682// else {
1683// data.getMember().getVariant().removeTag(Tags.SHIP_RECOVERABLE);
1684// }
1685 }
1686
1687 //System.out.println("Recoverable: " + result.size() + ", story: " + storyRecoverableShips.size());
1688
1689
1690 recoverableShips.clear();
1691 recoverableShips.addAll(result);
1692 return result;
1693 }
1694
1695
1696
1697
1698 public static void recoverShips(List<FleetMemberAPI> ships, FleetEncounterContext context, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet) {
1699
1700 if (!Misc.isPlayerOrCombinedContainingPlayer(winningFleet)) {
1701 return;
1702 }
1703
1704 DataForEncounterSide winnerData = null;
1705 DataForEncounterSide loserData = null;
1706
1707 if (context != null) {
1708 winnerData = context.getDataFor(winningFleet);
1709 loserData = context.getDataFor(otherFleet);
1710 }
1711
1713
1714 for (FleetMemberAPI member : ships) {
1715 //CampaignFleetAPI sourceFleet = context.getBattle().getSourceFleet(member);
1716 //repairFleetMember(member, sourceFleet == playerFleet);
1717
1718 if (member.getStatus().getNumStatuses() <= 1) {
1719 member.getStatus().repairDisabledABit();
1720 }
1721// for (int i = 1; i < member.getStatus().getNumStatuses(); i++) {
1722// if ((float) Math.random() > 0.33f) {
1723// member.getStatus().setDetached(i, true);
1724// member.getStatus().setHullFraction(i, 0f);
1725// }
1726// }
1727
1728 float minHull = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MIN, 0f);
1729 float maxHull = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MAX, 0f);
1730 float minCR = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MIN, 0f);
1731 float maxCR = playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MAX, 0f);
1732
1733 minHull += member.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MIN, 0f);
1734 maxHull += member.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MAX, 0f);
1735 minCR += member.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MIN, 0f);
1736 maxCR += member.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MAX, 0f);
1737
1738 float hull = (float) Math.random() * (maxHull - minHull) + minHull;
1739 if (hull < 0.01f) hull = 0.01f;
1740 if (hull > 1f) hull = 1f;
1741 member.getStatus().setHullFraction(hull);
1742
1743 float cr = (float) Math.random() * (maxCR - minCR) + minCR;
1744 if (cr < 0 || member.isMothballed()) cr = 0;
1745 float max = member.getRepairTracker() == null ? 1f : member.getRepairTracker().getMaxCR();
1746 if (cr > max) cr = max;
1747 member.getRepairTracker().setCR(cr);
1748
1749 if (winnerData != null) winnerData.getInReserveDuringLastEngagement().add(member);
1750 playerFleet.getFleetData().addFleetMember(member);
1751 if (context != null) {
1752 context.getBattle().getCombinedFor(playerFleet).getFleetData().addFleetMember(member);
1753 context.origSourceForRecoveredShips.put(member, context.getBattle().getSourceFleet(member));
1754 context.getBattle().getMemberSourceMap().put(member, playerFleet);
1755 }
1756
1757 member.setFleetCommanderForStats(null, null);
1758
1759
1760 member.setOwner(0);
1761
1762 if (!Misc.isUnremovable(member.getCaptain())) {
1763 member.setCaptain(Global.getFactory().createPerson());
1764 member.getCaptain().setFaction(Factions.PLAYER);
1765 }
1766
1767 //member.getRepairTracker().setMothballed(true);
1768
1769 if (winnerData != null) {
1770 winnerData.changeEnemy(member, Status.REPAIRED);
1771 winnerData.changeOwn(member, Status.REPAIRED);
1772
1773 winnerData.getDestroyedInLastEngagement().remove(member);
1774 winnerData.getDisabledInLastEngagement().remove(member);
1775 }
1776
1777 if (loserData != null) {
1778 loserData.changeEnemy(member, Status.REPAIRED);
1779 loserData.changeOwn(member, Status.REPAIRED);
1780
1781 loserData.getDestroyedInLastEngagement().remove(member);
1782 loserData.getDisabledInLastEngagement().remove(member);
1783 }
1784 }
1785
1786 return;
1787 }
1788
1789
1790 public static void prepareShipForRecovery(FleetMemberAPI member,
1791 boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods,
1792 float weaponRetainProb, float wingRetainProb, Random salvageRandom) {
1793 ShipVariantAPI variant = member.getVariant().clone();
1794 //variant.setOriginalVariant(null);
1795 if (retainAllHullmods) {
1796 // do nothing
1797 } else if (retainKnownHullmods) {
1798 for (String modId : new ArrayList<String>(variant.getHullMods())) {
1800 variant.removeMod(modId);
1801 }
1802 }
1803 } else {
1804 variant.clearHullMods();
1805 variant.setNumFluxCapacitors(0);
1806 variant.setNumFluxVents(0);
1807 }
1808
1809 if (clearSMods && !variant.hasTag(Tags.VARIANT_ALWAYS_RETAIN_SMODS_ON_SALVAGE)) {
1810 for (String id : new ArrayList<String>(variant.getSMods())) {
1811 variant.removePermaMod(id);
1812 }
1813 }
1814
1815 variant.setSource(VariantSource.REFIT);
1816 member.setVariant(variant, false, false);
1817 List<String> remove = new ArrayList<String>();
1818
1819// if (!retainHullmods) {
1820// variant.clearHullMods();
1821// variant.setNumFluxCapacitors(0);
1822// variant.setNumFluxVents(0);
1823// }
1824
1825 Random random = new Random();
1826 if (salvageRandom != null) random = salvageRandom;
1827
1828 if (!member.isFighterWing()) {
1829 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
1830 if (random.nextFloat() > weaponRetainProb) {
1831 remove.add(slotId);
1832 }
1833 }
1834 for (String slotId : remove) {
1835 variant.clearSlot(slotId);
1836 }
1837
1838 int index = 0;
1839 for (String id : variant.getFittedWings()) {
1840 if (random.nextFloat() > wingRetainProb) {
1841 variant.setWingId(index, null); // won't clear out built-in wings
1842 }
1843 index++;
1844 }
1845 }
1846
1847 for (String slotId : variant.getStationModules().keySet()) {
1848 prepareModuleForRecovery(member, slotId,
1849 retainAllHullmods, retainKnownHullmods, clearSMods, weaponRetainProb, wingRetainProb, salvageRandom);
1850 }
1851
1852
1853 for (int i = 1; i < member.getStatus().getNumStatuses(); i++) {
1854 if (random.nextFloat() > 0.5f) {
1855 member.getStatus().setDetached(i, false);
1856 member.getStatus().setHullFraction(i, 0.1f + 0.1f * random.nextFloat());
1857 }
1858 }
1859
1860 // get rid of any short-term modifiers, such as "0 repair rate during emergency burn"
1861 for (int i = 0; i < 10; i++) {
1862 member.getBuffManager().advance(1f);
1863 }
1864// for (Buff buff : new ArrayList<Buff>(member.getBuffManager().getBuffs())) {
1865// member.getBuffManager().removeBuff(buff.getId());
1866// }
1867
1868 variant.addTag(Tags.SHIP_RECOVERABLE);
1869 }
1870
1871 public static void prepareModuleForRecovery(FleetMemberAPI member, String moduleSlotId,
1872 boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods,
1873 float weaponRetainProb, float wingRetainProb, Random salvageRandom) {
1874
1875 ShipVariantAPI moduleCurrent = member.getVariant().getModuleVariant(moduleSlotId);
1876 if (moduleCurrent == null) return;
1877
1878 moduleCurrent = moduleCurrent.clone();
1879 moduleCurrent.setOriginalVariant(null);
1880 if (retainAllHullmods) {
1881 // do nothing
1882 } else if (retainKnownHullmods) {
1883 for (String modId : new ArrayList<String>(moduleCurrent.getHullMods())) {
1885 moduleCurrent.removeMod(modId);
1886 }
1887 }
1888 } else {
1889 moduleCurrent.clearHullMods();
1890 moduleCurrent.setNumFluxCapacitors(0);
1891 moduleCurrent.setNumFluxVents(0);
1892 }
1893
1894 if (clearSMods && !moduleCurrent.hasTag(Tags.VARIANT_ALWAYS_RETAIN_SMODS_ON_SALVAGE)) {
1895 for (String id : new ArrayList<String>(moduleCurrent.getSMods())) {
1896 moduleCurrent.removePermaMod(id);
1897 }
1898 }
1899
1900 moduleCurrent.setSource(VariantSource.REFIT);
1901 member.getVariant().setModuleVariant(moduleSlotId, moduleCurrent);
1902
1903 List<String> remove = new ArrayList<String>();
1904
1905 Random random = Misc.random;
1906 if (salvageRandom != null) random = salvageRandom;
1907
1908 for (String slotId : moduleCurrent.getNonBuiltInWeaponSlots()) {
1909 if (random.nextFloat() > weaponRetainProb) {
1910 remove.add(slotId);
1911 }
1912 }
1913 for (String slotId : remove) {
1914 moduleCurrent.clearSlot(slotId);
1915 }
1916
1917 int index = 0;
1918 for (String id : moduleCurrent.getFittedWings()) {
1919 if (random.nextFloat() > wingRetainProb) {
1920 moduleCurrent.setWingId(index, null); // won't clear out built-in wings
1921 }
1922 index++;
1923 }
1924 }
1925
1926
1927 public void gainXP() {
1928 if (sideData.size() != 2) return;
1929 if (!battle.isPlayerInvolved()) return;
1930
1931 DataForEncounterSide sideOne = sideData.get(0);
1932 DataForEncounterSide sideTwo = sideData.get(1);
1933 if (battle.isPlayerSide(battle.getSideFor(sideOne.getFleet()))) {
1934 gainXP(sideOne, sideTwo);
1935 } else if (battle.isPlayerSide(battle.getSideFor(sideTwo.getFleet()))) {
1936 gainXP(sideTwo, sideOne);
1937 }
1938 }
1939
1940 protected void gainOfficerXP(DataForEncounterSide data, float xp) {
1941 float max = data.getMaxTimeDeployed();
1942 if (max < 1) max = 1;
1943 float num = data.getOfficerData().size();
1944 if (num < 1) num = 1;
1945 for (PersonAPI person : data.getOfficerData().keySet()) {
1946 OfficerEngagementData oed = data.getOfficerData().get(person);
1947 if (oed.sourceFleet == null || !oed.sourceFleet.isPlayerFleet()) continue;
1948
1949 OfficerDataAPI od = oed.sourceFleet.getFleetData().getOfficerData(person);
1950 if (od == null) continue; // shouldn't happen, as this is checked earlier before it goes into the map
1951
1952 float f = oed.timeDeployed / max;
1953 if (f < 0) f = 0;
1954 if (f > 1) f = 1;
1955
1956 od.addXP((long)(f * xp / num), textPanelForXPGain);
1957 }
1958 }
1959
1960
1964
1966 this.playerFPHullDamageToEnemies = playerFPHullDamageToEnemies;
1967 }
1968
1971 }
1972
1974 this.allyFPHullDamageToEnemies = allyFPHullDamageToEnemies;
1975 }
1976
1977 protected float playerFPHullDamageToEnemies = 0f;
1978 protected float allyFPHullDamageToEnemies = 0f;
1979 protected float playerFPHullDamageToAllies = 0f;
1980 protected Map<FactionAPI, Float> playerFPHullDamageToAlliesByFaction = new HashMap<FactionAPI, Float>();
1981 protected void computeFPHullDamage() {
1982 if (runningDamageTotal == null) return;
1983
1984// playerFPHullDamageToEnemies = 0f;
1985// allyFPHullDamageToEnemies = 0f;
1986// playerFPHullDamageToAllies = 0f;
1987
1988 for (FleetMemberAPI member : runningDamageTotal.getDealt().keySet()) {
1989 if (member.getOwner() != 0) continue;
1990
1991
1992 DealtByFleetMember dealt = runningDamageTotal.getDealt().get(member);
1993 for (FleetMemberAPI target : dealt.getDamage().keySet()) {
1994 if (battle.getSourceFleet(target) == null) continue;
1995
1996 DamageToFleetMember damage = dealt.getDamageTo(target);
1997 float maxHull = target.getStats().getHullBonus().computeEffective(target.getHullSpec().getHitpoints());
1998 if (maxHull <= 0) continue;
1999 if (target.isFighterWing()) {
2000 maxHull *= target.getNumFightersInWing();
2001 }
2002
2003 float currDam = Math.min(damage.hullDamage, maxHull) / maxHull * (float) target.getFleetPointCost();
2004 if (target.getOwner() == 1) {
2005 CampaignFleetAPI fleet = battle != null ? battle.getSourceFleet(member) : null;
2006 boolean ally = member.isAlly();
2007 if (ally && fleet != null &&
2008 fleet.getFaction() != null && fleet.getFaction().isPlayerFaction()) {
2009 ally = false;
2010 }
2011 if (ally) {
2012 allyFPHullDamageToEnemies += currDam;
2013 } else {
2014 playerFPHullDamageToEnemies += currDam;
2015 }
2016 } else if (!member.isAlly() && target.isAlly() && !target.isFighterWing()) {
2017 playerFPHullDamageToAllies += currDam;
2018 CampaignFleetAPI fleet = battle != null ? battle.getSourceFleet(target) : null;
2019 if (fleet != null) {
2020 float curr = currDam;
2021 if (playerFPHullDamageToAlliesByFaction.containsKey(fleet.getFaction())) {
2023 }
2025 }
2026 }
2027 }
2028 }
2029
2030// if (playerFPHullDamageToEnemies <= 0) {
2031// System.out.println("HERE 12523423");
2032// }
2033// allyFPHullDamageToEnemies += playerFPHullDamageToEnemies;
2034// playerFPHullDamageToEnemies = 0f;
2035 runningDamageTotal = null;
2036 }
2037
2038
2041 if (total <= 0) {
2042 if (battle == null) return 1f;
2043 if (battle.isPlayerInvolved() && (battle.getPlayerSideSnapshot().size() <= 1 || battle.getPlayerSide().size() <= 1)) return 1f;
2044 return 0f;
2045 }
2046
2047 boolean hasAllies = false;
2048 boolean startedWithAllies = false;
2049 if (battle != null) {
2050 hasAllies = battle.getPlayerSide().size() <= 1;
2051 startedWithAllies = battle.getPlayerSideSnapshot().size() > 1;
2052 }
2053 if (startedWithAllies) { // && hasAllies) {
2054 //return Math.min(0.9f, playerFPHullDamageToEnemies / total);
2055 return Math.min(1f, playerFPHullDamageToEnemies / total);
2056 } else {
2057 return 1f;
2058 }
2059 }
2060
2061 protected float xpGained = 0;
2062 protected void gainXP(DataForEncounterSide side, DataForEncounterSide otherSide) {
2063 float bonusXP = 0f;
2064 float points = 0f;
2065 for (FleetMemberData data : side.getOwnCasualties()) {
2066 if (data.getStatus() == Status.DISABLED ||
2067 data.getStatus() == Status.DESTROYED) {
2068 float [] bonus = Misc.getBonusXPForScuttling(data.getMember());
2069 points += bonus[0];
2070 bonusXP += bonus[1] * bonus[0];
2071 }
2072 }
2073 if (bonusXP > 0 && points > 0) {
2074 points = 1;
2076 Global.getSector().getPlayerStats().setBonusXPGainReason("from losing s-modded ships");
2077 Global.getSector().getPlayerStats().spendStoryPoints((int)Math.round(points), true, textPanelForXPGain, false, bonusXP, null);
2080 }
2081
2082 //CampaignFleetAPI fleet = side.getFleet();
2084 float fpTotal = 0;
2085 for (FleetMemberData data : otherSide.getOwnCasualties()) {
2086 float fp = data.getMember().getFleetPointCost();
2087
2088// String prefix = "xp_mult_";
2089// for (String tag : data.getMember().getHullSpec().getTags()) {
2090// if (tag.startsWith(prefix)) {
2091// tag = tag.replaceFirst(prefix, "");
2092// fp *= Float.parseFloat(tag);
2093// break;
2094// }
2095// }
2096
2097 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2098 fpTotal += fp;
2099 }
2100
2101 float xp = (float) fpTotal * 250;
2102 xp *= 2f;
2103
2104 float difficultyMult = Math.max(1f, difficulty);
2105 xp *= difficultyMult;
2106
2108
2109 xp *= Global.getSettings().getFloat("xpGainMult");
2110
2111
2112 if (xp > 0) {
2113 //fleet.getCargo().gainCrewXP(xp);
2114
2115 //if (side.getFleet().isPlayerFleet()) {
2116 //}
2117 // only gain XP if it's the player fleet anyway, no need to check this here
2118 gainOfficerXP(side, xp);
2119
2120 fleet.getCommander().getStats().addXP((long) xp, textPanelForXPGain);
2122
2123 xpGained = xp;
2124 }
2125 }
2126
2127 public void addPotentialOfficer() {
2128 if (!isEngagedInHostilities()) return;
2129 if (xpGained <= 0) return;
2130 if (sideData.size() != 2) return;
2131 if (!battle.isPlayerInvolved()) return;
2132
2133 DataForEncounterSide sideOne = sideData.get(0);
2134 DataForEncounterSide sideTwo = sideData.get(1);
2135
2136 DataForEncounterSide player = sideOne;
2137 DataForEncounterSide enemy = sideTwo;
2138 if (battle.isPlayerSide(battle.getSideFor(sideTwo.getFleet()))) {
2139 player = sideTwo;
2140 enemy = sideOne;
2141 }
2142
2143 float fpDestroyed = 0;
2144 for (FleetMemberData data : enemy.getOwnCasualties()) {
2145 float fp = data.getMember().getFleetPointCost();
2146 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2147 fpDestroyed += fp;
2148 }
2149 fpDestroyed *= computePlayerContribFraction();
2150 for (FleetMemberData data : player.getOwnCasualties()) {
2151 if (data.getMember().isAlly()) continue;
2152 float fp = data.getMember().getFleetPointCost();
2153 fp *= 1f + data.getMember().getCaptain().getStats().getLevel() / 5f;
2154 fpDestroyed += fp;
2155 }
2156
2157 float fpInFleet = 0f;
2159 float fp = member.getFleetPointCost();
2160 fp *= 1f + member.getCaptain().getStats().getLevel() / 5f;
2161 fpInFleet += fp;
2162 }
2163
2164
2165 float maxProb = Global.getSettings().getFloat("maxOfficerPromoteProb");
2166 float probMult = Global.getSettings().getFloat("officerPromoteProbMult");
2169
2170 float prob = fpDestroyed / (Math.max(1f, fpInFleet));
2171 //prob /= 5f;
2172 prob *= probMult;
2173
2174 if (curr >= max) prob *= 0.5f;
2175 if (prob > maxProb) prob = maxProb;
2176
2177 Random random = Misc.random;
2178 if (salvageRandom != null) {
2179 random = salvageRandom;
2180 }
2181
2183 prob = 0f;
2184 }
2185
2186 if (random.nextFloat() < prob) {
2189 }
2190 }
2191
2192
2194 protected int creditsLooted = 0;
2195 public void generateLoot(List<FleetMemberAPI> recoveredShips, boolean withCredits) {
2196 creditsLooted = 0;
2197 loot.clear();
2198 //if (getWinner() == Global.getSector().getPlayerFleet()) {
2200 generatePlayerLoot(recoveredShips, withCredits);
2201 } else { //if (getLoser() == Global.getSector().getPlayerFleet()) {
2202 handleCargoLooting(recoveredShips, true);
2203 }
2204 loot.sort();
2205 }
2206
2207 private Random salvageRandom = null;
2208 public Random getSalvageRandom() {
2209 return salvageRandom;
2210 }
2211 public void setSalvageRandom(Random salvageRandom) {
2212 this.salvageRandom = salvageRandom;
2213 }
2214
2215 protected void generatePlayerLoot(List<FleetMemberAPI> recoveredShips, boolean withCredits) {
2216 //computeFPHullDamage();
2217
2218
2219 DataForEncounterSide winner = getWinnerData();
2220 DataForEncounterSide loser = getLoserData();
2221
2222 if (winner == null || loser == null) return;
2223
2224 float adjustedFPSalvage = 0;
2225 float playerContribMult = computePlayerContribFraction();
2226
2227 Random origSalvageRandom = salvageRandom;
2228 long extraSeed = 1340234324325L;
2229 if (origSalvageRandom != null) extraSeed = origSalvageRandom.nextLong();
2230
2231 for (FleetMemberData data : winner.getEnemyCasualties()) {
2232 if (data.getStatus() == Status.REPAIRED) {
2233 continue;
2234 }
2235
2236 if (data.getMember() != null && data.getMember().getHullSpec().hasTag(Tags.NO_BATTLE_SALVAGE)) {
2237 continue;
2238 }
2239
2240 if (origSalvageRandom != null) {
2241 String sig = data.getMember().getHullId();
2242 if (data.getMember().getVariant() != null) {
2243 for (WeaponGroupSpec spec : data.getMember().getVariant().getWeaponGroups()) {
2244 for (String slotId : spec.getSlots()) {
2245 String w = data.getMember().getVariant().getWeaponId(slotId);
2246 if (w != null) sig += w;
2247 }
2248 }
2249 }
2250 if (loser != null && loser.getFleet() != null && loser.getFleet().getFleetData() != null) {
2251 List<FleetMemberAPI> members = loser.getFleet().getFleetData().getMembersListCopy();
2252 if (members != null) {
2253 int index = members.indexOf(data.getMember());
2254 if (index >= 0) {
2255 sig += "" + index;
2256 }
2257 }
2258 }
2259 long seed = sig.hashCode() * 143234234234L * extraSeed;
2260 salvageRandom = new Random(seed);
2261 //System.out.println("Seed for " + data.getMember() + ": " + seed);
2262 }
2263
2264 float mult = getSalvageMult(data.getStatus()) * playerContribMult;
2265 lootWeapons(data.getMember(), data.getMember().getVariant(), false, mult, false);
2266 lootHullMods(data.getMember(), data.getMember().getVariant(), mult);
2267 lootWings(data.getMember(), data.getMember().getVariant(), false, mult);
2268 adjustedFPSalvage += (float) data.getMember().getFleetPointCost() * mult;
2269 }
2270
2271 for (FleetMemberData data : winner.getOwnCasualties()) {
2272 if (data.getMember().isAlly()) continue;
2273
2274 if (data.getStatus() == Status.CAPTURED || data.getStatus() == Status.REPAIRED) {
2275 continue;
2276 }
2277
2278 if (data.getMember() != null && data.getMember().getHullSpec().hasTag(Tags.NO_BATTLE_SALVAGE)) {
2279 continue;
2280 }
2281
2282 // only care about salvageRandom for enemy casualties, not player
2283// if (origSalvageRandom != null) {
2284// salvageRandom = new Random(data.getMember().getId().hashCode() * 143234234234L * extraSeed);
2285// }
2286
2287 float mult = getSalvageMult(data.getStatus());
2288 lootWeapons(data.getMember(), data.getMember().getVariant(), true, mult, false);
2289 lootWings(data.getMember(), data.getMember().getVariant(), true, mult);
2290
2291 adjustedFPSalvage += (float) data.getMember().getFleetPointCost() * mult;
2292 }
2293
2294 if (recoveredShips != null) {
2295 for (FleetMemberAPI member : recoveredShips) {
2296 float mult = getSalvageMult(Status.CAPTURED);
2297 adjustedFPSalvage += (float) member.getFleetPointCost() * mult;
2298 }
2299 }
2300
2301 salvageRandom = origSalvageRandom;
2302
2303 // don't want salvageRandom to be influenced by the number of losses on either side
2304 Random resetSalvageRandomTo = null;
2305 Random forRandomDrops = null;
2306 Random forCargoDrops = null;
2307
2308 Random random = Misc.random;
2309 if (salvageRandom != null) {
2310 random = salvageRandom;
2311 resetSalvageRandomTo = Misc.getRandom(random.nextLong(), 11);
2312 forRandomDrops = Misc.getRandom(random.nextLong(), 17);
2313 forCargoDrops = Misc.getRandom(random.nextLong(), 31);
2314 } else {
2315 if (getBattle() != null) {
2317 if (memory.contains(MemFlags.SALVAGE_SEED)) {
2318 random = new Random(memory.getLong(MemFlags.SALVAGE_SEED));
2319 }
2320 }
2321 }
2322
2323 float minCreditsFraction = Global.getSettings().getFloat("salvageFractionCreditsMin");
2324 float maxCreditsFraction = Global.getSettings().getFloat("salvageFractionCreditsMax");
2325
2326 float creditsFraction = minCreditsFraction + (maxCreditsFraction - minCreditsFraction) * random.nextFloat();
2327 creditsFraction *= playerContribMult;
2328
2329 float maxSalvageValue = adjustedFPSalvage * Global.getSettings().getFloat("salvageValuePerFP");
2330 if (Misc.isEasy()) {
2331 maxSalvageValue *= Global.getSettings().getFloat("easySalvageMult");
2332 }
2333
2335 float valueMultFleet = playerFleet.getStats().getDynamic().getValue(Stats.BATTLE_SALVAGE_MULT_FLEET);
2336 float valueModShips = getSalvageValueModPlayerShips();
2337
2338
2339 maxSalvageValue *= valueMultFleet + valueModShips;
2340
2341 creditsLooted = Math.round(maxSalvageValue * creditsFraction);
2342 if (!withCredits) creditsLooted = 0;
2343 maxSalvageValue -= creditsLooted;
2344
2345 float salvageValue = 0f;
2347 lootPicker.add(Commodities.METALS, 20);
2348 lootPicker.add(Commodities.SUPPLIES, 10);
2349 lootPicker.add(Commodities.FUEL, 10);
2350 lootPicker.add(Commodities.HEAVY_MACHINERY, 1);
2351
2352 while (salvageValue < maxSalvageValue) {
2353 String commodityId = lootPicker.pick();
2354 if (commodityId == null) break;
2355
2357 float qty = 1f;
2358 salvageValue += spec.getBasePrice() * qty;
2359 loot.addCommodity(commodityId, qty);
2360 }
2361
2362
2363 float fuelMult = playerFleet.getStats().getDynamic().getValue(Stats.FUEL_SALVAGE_VALUE_MULT_FLEET);
2364 float fuel = loot.getFuel();
2365 if (fuelMult > 1f) {
2366 loot.addFuel((int) Math.round(fuel * (fuelMult - 1f)));
2367 }
2368
2369 if (getBattle().getSnapshotSideFor(loser.getFleet()) == null) return;
2370
2371 List<DropData> dropRandom = new ArrayList<DropData>();
2372 List<DropData> dropValue = new ArrayList<DropData>();
2373 //for (CampaignFleetAPI other : getBattle().getSideFor(loser.getFleet())) {
2374 for (CampaignFleetAPI other : getBattle().getSnapshotSideFor(loser.getFleet())) {
2375 dropRandom.addAll(other.getDropRandom());
2376 dropValue.addAll(other.getDropValue());
2377 other.getDropRandom().clear();
2378 other.getDropValue().clear();
2379
2381 loot.addAll(extra);
2382
2384 if (!extra.isEmpty()) {
2386 }
2387 }
2388
2389 if (forRandomDrops != null) {
2390 random = forRandomDrops;
2391 }
2392 CargoAPI extra = SalvageEntity.generateSalvage(random, valueMultFleet + valueModShips, 1f, fuelMult, dropValue, dropRandom);
2393 for (CargoStackAPI stack : extra.getStacksCopy()) {
2394 loot.addFromStack(stack);
2395 }
2396
2397 if (forCargoDrops != null) {
2398 salvageRandom = forCargoDrops;
2399 }
2400 handleCargoLooting(recoveredShips, false);
2401
2402 if (resetSalvageRandomTo != null) {
2403 salvageRandom = resetSalvageRandomTo;
2404 }
2405 }
2406
2409// float valueModShips = 0;
2410// for (FleetMemberAPI member : Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy()) {
2411// if (member.isMothballed()) continue;
2412// float maxCurr = member.getStats().getDynamic().getValue(Stats.BATTLE_SALVAGE_VALUE_MULT_MOD, 0f);
2413// OfficerEngagementData data = getWinnerData().getFleetMemberDeploymentData().get(member);
2414// if (data == null) continue;
2415// float memberDeployed = data.timeDeployed;
2416// float maxDeployed = getWinnerData().getMaxTimeDeployed();
2417// if (maxDeployed <= 0) continue;
2418// maxCurr *= Math.min(1f, memberDeployed / maxDeployed);
2419//
2420// valueModShips += maxCurr;
2421// }
2422// return valueModShips;
2423 }
2424
2425 protected static class LootableCargoStack {
2426 public CargoAPI source;
2427 public CargoStackAPI stack;
2428 public LootableCargoStack(CargoAPI source, CargoStackAPI stack) {
2429 this.source = source;
2430 this.stack = stack;
2431 }
2432 }
2433
2434 protected static class LossFraction {
2435 public float maxCargo;
2436 public float maxFuel;
2437 public float lostCargo;
2438 public float lostFuel;
2439 }
2440
2441
2442 protected void handleCargoLooting(List<FleetMemberAPI> recoveredShips, boolean takingFromPlayer) {
2443 DataForEncounterSide winner = getWinnerData();
2444 DataForEncounterSide loser = getLoserData();
2445
2446 if (winner == null || loser == null) return;
2447
2448 loser.getFleet().getFleetData().updateCargoCapacities();
2449 CargoAPI loserCargo = (CargoAPI) loser.getFleet().getCargo();
2450 float maxCargo = loserCargo.getMaxCapacity();
2451 float maxFuel = loserCargo.getMaxFuel();
2452
2453 Random random = Misc.random;
2454 if (salvageRandom != null) random = salvageRandom;
2455
2456// boolean playerLost = battle.isPlayerSide(battle.getSideFor(loser.getFleet()));
2457
2458 Map<CargoAPI, LossFraction> fractions = new HashMap<CargoAPI, LossFraction>();
2459
2460 float lostCargo = 0f;
2461 float lostFuel = 0f;
2462
2463 float totalLoss = 0f;
2464 for (FleetMemberData data : winner.getEnemyCasualties()) {
2465// if (data.getStatus() == Status.REPAIRED) {
2466// continue;
2467// }
2468// if (playerLost && data.getMember().isAlly()) {
2469// continue;
2470// }
2471
2472 CampaignFleetAPI source = battle.getSourceFleet(data.getMember());
2473 CampaignFleetAPI orig = origSourceForRecoveredShips.get(data.getMember());
2474 if (orig != null) source = orig;
2475
2476 if (source != null) {
2477 CargoAPI c = source.getCargo();
2478 LossFraction loss = fractions.get(c);
2479 if (loss == null) {
2480 loss = new LossFraction();
2481 loss.maxCargo = c.getMaxCapacity();
2482 loss.maxFuel = c.getMaxFuel();
2483 fractions.put(c, loss);
2484 }
2485
2486 loss.lostCargo += data.getMember().getCargoCapacity();
2487 loss.lostFuel += data.getMember().getFuelCapacity();
2488
2489 loss.maxCargo += data.getMember().getCargoCapacity();
2490 loss.maxFuel += data.getMember().getFuelCapacity();
2491
2492 totalLoss += loss.maxCargo + loss.maxFuel;
2493 } else {
2494 lostCargo += data.getMember().getCargoCapacity();
2495 lostFuel += data.getMember().getFuelCapacity();
2496
2497 maxCargo += data.getMember().getCargoCapacity();
2498 maxFuel += data.getMember().getFuelCapacity();
2499
2500 totalLoss += maxCargo + maxFuel;
2501 }
2502
2503 }
2504
2505 //if (lostCargo <= 0 && lostFuel <= 0) {
2506 if (totalLoss <= 0) {
2507 return;
2508 }
2509
2510 if (maxCargo < 1) maxCargo = 1;
2511 if (maxFuel < 1) maxFuel = 1;
2512
2513 float recoveryFraction = Global.getSettings().getFloat("salvageCargoFraction");
2514
2515 if (battle.isPlayerSide(battle.getSideFor(winner.getFleet()))) {
2516 float playerContribMult = computePlayerContribFraction();
2517 recoveryFraction *= playerContribMult;
2518 }
2519
2520 float cargoFractionLost = lostCargo / maxCargo;
2521 float fuelFractionLost = lostFuel / maxFuel;
2522 if (lostCargo > maxCargo) cargoFractionLost = 1f;
2523 if (lostFuel > maxFuel) fuelFractionLost = 1f;
2524
2525
2526 List<CampaignFleetAPI> losers = battle.getSnapshotSideFor(loser.getFleet());
2527 if (losers == null) return;
2528
2529 List<LootableCargoStack> stacks = new ArrayList<LootableCargoStack>();
2530 for (CampaignFleetAPI curr : losers) {
2531 for (CargoStackAPI stack : curr.getCargo().getStacksCopy()) {
2532 stacks.add(new LootableCargoStack(curr.getCargo(), stack));
2533 }
2534 }
2535
2536 for (LootableCargoStack stack : stacks) {
2537 if (stack.stack.isNull()) continue;
2538 if (stack.stack.isPersonnelStack()) continue;
2539 if (stack.stack.getSize() < 1) continue;
2540
2541 float actualCargoFractionLost = cargoFractionLost;
2542 float actualFuelFractionLost = fuelFractionLost;
2543 LossFraction loss = fractions.get(stack.source);
2544 if (loss != null) {
2545 actualCargoFractionLost = loss.lostCargo / loss.maxCargo;
2546 actualFuelFractionLost = loss.lostFuel / loss.maxFuel;
2547 if (loss.lostCargo > loss.maxCargo) actualCargoFractionLost = 1f;
2548 if (loss.lostFuel > loss.maxFuel) actualFuelFractionLost = 1f;
2549 }
2550
2551
2552 if (takingFromPlayer) {
2553 if (stack.stack.isSpecialStack()) continue;
2554 if (stack.stack.isCommodityStack()) {
2555 CommoditySpecAPI spec = stack.stack.getResourceIfResource();
2556 if (spec != null && spec.hasTag(Commodities.TAG_NO_LOSS_FROM_COMBAT)){
2557 continue;
2558 }
2559 }
2560 }
2561
2562 float numLost = 0;
2563 float numTaken = 0;
2564 if (stack.stack.isFuelStack()) {
2565 numLost = actualFuelFractionLost * stack.stack.getSize();
2566 numTaken = Math.round(numLost * (0.5f + random.nextFloat() * 0.5f));
2567 } else {
2568 numLost = actualCargoFractionLost * stack.stack.getSize();
2569 numTaken = Math.round(numLost * (0.5f + random.nextFloat() * 0.5f));
2570 }
2571
2572 if (numLost < 1) {
2573 if (random.nextFloat() < numLost) {
2574 numLost = 1;
2575 } else {
2576 numLost = 0;
2577 numTaken = 0;
2578 }
2579 }
2580
2581 if (numLost <= 0) continue;
2582
2583 stack.stack.add(-numLost);
2584 if (numTaken * recoveryFraction >= 1) {
2585 loot.addItems(stack.stack.getType(), stack.stack.getData(), numTaken * recoveryFraction);
2586 }
2587 }
2588
2589 for (CampaignFleetAPI fleet : battle.getSideFor(loser.getFleet())) {
2590 if (fleet.isPlayerFleet()) {
2591 fleet.getCargo().sort();
2592 break;
2593 }
2594 }
2595 }
2596
2597
2599 return loot;
2600 }
2601
2602 protected void lootHullMods(FleetMemberAPI member, ShipVariantAPI variant, float mult) {
2603 if (variant == null) return;
2604 if (member.isFighterWing()) return;
2605 Random random = Misc.random;
2606 if (salvageRandom != null) random = salvageRandom;
2607
2608 float p = Global.getSettings().getFloat("salvageHullmodProb");
2609 float pItem = Global.getSettings().getFloat("salvageHullmodRequiredItemProb");
2610
2611 for (String id : variant.getHullMods()) {
2612 if (!variant.getHullSpec().isBuiltInMod(id)) {
2613 if (random.nextFloat() < pItem && random.nextFloat() < mult) {
2615 CargoStackAPI item = spec.getEffect().getRequiredItem();
2616 if (item != null) {
2617 boolean addToLoot = true;
2619 addToLoot = false;
2620 } else if (item.getResourceIfResource() != null && item.getResourceIfResource().hasTag(Tags.NO_DROP)) {
2621 addToLoot = false;
2622 } else if (item.getFighterWingSpecIfWing() != null && item.getFighterWingSpecIfWing().hasTag(Tags.NO_DROP)) {
2623 addToLoot = false;
2624 } else if (item.getWeaponSpecIfWeapon() != null && item.getWeaponSpecIfWeapon().hasTag(Tags.NO_DROP)) {
2625 addToLoot = false;
2626 }
2627 if (addToLoot) {
2628 loot.addItems(item.getType(), item.getData(), 1);
2629 }
2630 }
2631 }
2632 }
2633
2634 //if (random.nextFloat() > mult) continue;
2635 if (random.nextFloat() < p && random.nextFloat() < mult) {
2637 boolean known = Global.getSector().getPlayerFaction().knowsHullMod(id);
2638 if (DebugFlags.ALLOW_KNOWN_HULLMOD_DROPS) known = false;
2639 if (known || spec.isHidden() || spec.isHiddenEverywhere()) continue;
2640 //if (spec.isAlwaysUnlocked()) continue;
2641 if (spec.hasTag(Tags.HULLMOD_NO_DROP)) continue;
2642
2643 loot.addHullmods(id, 1);
2644 }
2645 }
2646
2647 for (String slotId : variant.getModuleSlots()) {
2648 WeaponSlotAPI slot = variant.getSlot(slotId);
2649 if (slot.isStationModule()) {
2650 ShipVariantAPI module = variant.getModuleVariant(slotId);
2651 if (module == null) continue;
2652 lootHullMods(member, module, mult);
2653 }
2654 }
2655 }
2656
2657 protected void lootWings(FleetMemberAPI member, ShipVariantAPI variant, boolean own, float mult) {
2658 if (variant == null) return;
2659 if (member.isFighterWing()) return;
2660 Random random = Misc.random;
2661 if (salvageRandom != null) random = salvageRandom;
2662
2663 float p = Global.getSettings().getFloat("salvageWingProb");
2664 if (own) {
2665 p = Global.getSettings().getFloat("salvageOwnWingProb");
2667 } else {
2669 }
2670
2671 boolean alreadyStripped = recoverableShips.contains(member);
2672
2673 for (String id : variant.getNonBuiltInWings()) {
2674 if (!alreadyStripped) {
2675 if (random.nextFloat() > mult) continue;
2676 if (random.nextFloat() > p) continue;
2677 }
2678
2680 if (spec.hasTag(Tags.WING_NO_DROP)) continue;
2681 loot.addItems(CargoItemType.FIGHTER_CHIP, id, 1);
2682 }
2683
2684 for (String slotId : variant.getModuleSlots()) {
2685 WeaponSlotAPI slot = variant.getSlot(slotId);
2686 if (slot.isStationModule()) {
2687 ShipVariantAPI module = variant.getModuleVariant(slotId);
2688 if (module == null) continue;
2689 lootWings(member, module, own, mult);
2690 }
2691 }
2692 }
2693
2694 protected void lootWeapons(FleetMemberAPI member, ShipVariantAPI variant, boolean own, float mult, boolean lootingModule) {
2695 if (variant == null) return;
2696 if (member.isFighterWing()) return;
2697
2698// if (own) {
2699// System.out.println("238034wefwef");
2700// }
2701 //isUnremovable(
2702 if (own && !lootingModule && member.getCaptain() != null &&
2703 member.getCaptain().getMemoryWithoutUpdate().contains("$aiCoreIdForRecovery") &&
2704 //member.getCaptain().isAICore() &&
2705 !Misc.isUnremovable(member.getCaptain())) {
2706 //loot.addItems(CargoItemType.RESOURCES, member.getCaptain().getAICoreId(), 1);
2707 loot.addItems(CargoItemType.RESOURCES,
2708 member.getCaptain().getMemoryWithoutUpdate().getString("$aiCoreIdForRecovery"), 1);
2709 }
2710
2711 if (own) {
2713 }
2714
2715
2716 Random random = Misc.random;
2717 if (salvageRandom != null) random = salvageRandom;
2718
2719 String coreIdOverride = null;
2720 if (member.getCaptain() != null &&
2721 member.getCaptain().getMemoryWithoutUpdate().contains("$aiCoreIdForPossibleRecovery")) {
2722 coreIdOverride = member.getCaptain().getMemoryWithoutUpdate().getString("$aiCoreIdForPossibleRecovery");
2723 }
2724 if (!own && !lootingModule &&
2725 (member.getCaptain().isAICore() || coreIdOverride != null) &&
2727 String cid = member.getCaptain().getAICoreId();
2728 if (coreIdOverride != null) {
2729 cid = coreIdOverride;
2730 }
2731 if (cid != null) {
2733 if (!spec.hasTag(Tags.NO_DROP)) {
2734 float prob = Global.getSettings().getFloat("drop_prob_officer_" + cid);
2735 if (member.isStation()) {
2736 prob *= Global.getSettings().getFloat("drop_prob_mult_ai_core_station");
2737 } else if (member.isFrigate()) {
2738 prob *= Global.getSettings().getFloat("drop_prob_mult_ai_core_frigate");
2739 } else if (member.isDestroyer()) {
2740 prob *= Global.getSettings().getFloat("drop_prob_mult_ai_core_destroyer");
2741 } else if (member.isCruiser()) {
2742 prob *= Global.getSettings().getFloat("drop_prob_mult_ai_core_cruiser");
2743 } else if (member.isCapital()) {
2744 prob *= Global.getSettings().getFloat("drop_prob_mult_ai_core_capital");
2745 }
2746 if (prob > 0 && random.nextFloat() < prob) {
2747 loot.addItems(CargoItemType.RESOURCES, cid, 1);
2748 }
2749 }
2750 }
2751
2752 }
2753
2754 float p = Global.getSettings().getFloat("salvageWeaponProb");
2755 if (own) {
2756 p = Global.getSettings().getFloat("salvageOwnWeaponProb");
2758 } else {
2760 }
2761 boolean alreadyStripped = recoverableShips.contains(member);
2762
2763
2764 Set<String> remove = new HashSet<String>();
2765
2766 // there's another failsafe for OMEGA specifically, see SalvageDefenderInteraction.postPlayerSalvageGeneration()
2768 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
2769 String weaponId = variant.getWeaponId(slotId);
2770 if (weaponId == null) continue;
2771 if (loot.getNumWeapons(weaponId) <= 0) {
2772 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(weaponId);
2773 if (spec.hasTag(Tags.NO_DROP)) continue;
2774
2775 loot.addWeapons(weaponId, 1);
2776 remove.add(slotId);
2777 }
2778 }
2779 }
2780
2781 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
2782 if (remove.contains(slotId)) continue;
2783 //if ((float) Math.random() * mult > 0.75f) {
2784 if (!alreadyStripped) {
2785 if (random.nextFloat() > mult) continue;
2786 if (random.nextFloat() > p) continue;
2787 }
2788
2789 String weaponId = variant.getWeaponId(slotId);
2790 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(weaponId);
2791 if (spec.hasTag(Tags.NO_DROP)) continue;
2792
2793 loot.addItems(CargoAPI.CargoItemType.WEAPONS, weaponId, 1);
2794 remove.add(slotId);
2795 }
2796
2797
2798 for (String slotId : variant.getModuleSlots()) {
2799 WeaponSlotAPI slot = variant.getSlot(slotId);
2800 if (slot.isStationModule()) {
2801 ShipVariantAPI module = variant.getModuleVariant(slotId);
2802 if (module == null) continue;
2803 lootWeapons(member, module, own, mult, true);
2804 }
2805 }
2806 // DO NOT DO THIS - no point in removing them here since the ship is scrapped
2807 // and would need to clone the variant to do this right
2808// for (String slotId : remove) {
2809// variant.clearSlot(slotId);
2810// }
2811 //System.out.println("Cleared variant: " + variant.getHullVariantId());
2812 }
2813
2814 public void autoLoot() {
2815 DataForEncounterSide winner = getWinnerData();
2816 DataForEncounterSide loser = getLoserData();
2817 if (winner == null || loser == null) return;
2818
2819 List<CampaignFleetAPI> winners = battle.getSideFor(winner.getFleet());
2821 for (CampaignFleetAPI curr : winners) {
2822 picker.add(curr, curr.getFleetPoints());
2823 }
2824 for (CargoStackAPI stack : loot.getStacksCopy()) {
2825 if (stack.isNull() || stack.isFuelStack()) continue;
2826
2827 CampaignFleetAPI pick = picker.pick();
2828 if (pick == null) break;
2829
2830 CargoAPI winnerCargo = pick.getCargo();
2831 float spaceLeft = winnerCargo.getSpaceLeft();
2832 if (spaceLeft <= 0) {
2833 picker.remove(pick);
2834 continue;
2835 }
2836
2837 float spacePerUnit = stack.getCargoSpacePerUnit();
2838 float maxUnits = (int) (spaceLeft / spacePerUnit);
2839 if (maxUnits > stack.getSize()) maxUnits = stack.getSize();
2840 maxUnits = Math.round(maxUnits * (Math.random() * 0.5f + 0.5f));
2841 winnerCargo.addItems(stack.getType(), stack.getData(), maxUnits);
2842 }
2843
2844 picker.clear();
2845 for (CampaignFleetAPI curr : winners) {
2846 picker.add(curr, curr.getFleetPoints());
2847 }
2848 for (CargoStackAPI stack : loot.getStacksCopy()) {
2849 if (stack.isNull() || !stack.isFuelStack()) continue;
2850
2851 CampaignFleetAPI pick = picker.pick();
2852 if (pick == null) break;
2853
2854 CargoAPI winnerCargo = pick.getCargo();
2855 float spaceLeft = winnerCargo.getMaxCapacity() - winnerCargo.getFuel();
2856 if (spaceLeft <= 0) {
2857 picker.remove(pick);
2858 continue;
2859 }
2860
2861 float spacePerUnit = stack.getCargoSpacePerUnit();
2862 float maxUnits = (int) (spaceLeft / spacePerUnit);
2863 if (maxUnits > stack.getSize()) maxUnits = stack.getSize();
2864 maxUnits = Math.round(maxUnits * (Math.random() * 0.5f + 0.5f));
2865 winnerCargo.addItems(stack.getType(), stack.getData(), maxUnits);
2866 }
2867 }
2868
2869 public boolean hasWinnerAndLoser() {
2870 return getWinner() != null && getLoser() != null;
2871 }
2872
2874 return getWinnerData() != null ? getWinnerData().getFleet() : null;
2875 }
2877 return getLoserData() != null ? getLoserData().getFleet() : null;
2878 }
2879
2881 return fleet.getFleetData().getMinBurnLevel() >= other.getFleetData().getMaxBurnLevel() + 1f;
2882 }
2883
2884
2886// applyCrewAndShipLosses(result);
2887// fixFighters(result.getWinnerResult());
2888// fixFighters(result.getLoserResult());
2889 applyShipLosses(result);
2890 applyCrewLosses(result);
2891 }
2892
2893
2895 Set<CampaignFleetAPI> fleetsWithDecks = new HashSet<CampaignFleetAPI>();
2896 for (FleetMemberAPI curr : result.getReserves()) {
2897 if (battle.getSourceFleet(curr) == null) continue;
2898 if (curr.isMothballed()) continue;
2899 if (curr.getNumFlightDecks() > 0) {
2900 fleetsWithDecks.add(battle.getSourceFleet(curr));
2901 }
2902 }
2903 for (FleetMemberAPI curr : result.getDeployed()) {
2904 if (battle.getSourceFleet(curr) == null) continue;
2905 if (curr.isMothballed()) continue;
2906 if (curr.getNumFlightDecks() > 0) {
2907 fleetsWithDecks.add(battle.getSourceFleet(curr));
2908 }
2909 }
2910 for (FleetMemberAPI curr : result.getRetreated()) {
2911 if (battle.getSourceFleet(curr) == null) continue;
2912 if (curr.isMothballed()) continue;
2913 if (curr.getNumFlightDecks() > 0) {
2914 fleetsWithDecks.add(battle.getSourceFleet(curr));
2915 }
2916 }
2917
2918 List<FleetMemberAPI> saved = new ArrayList<FleetMemberAPI>();
2919 for (FleetMemberAPI curr : result.getDestroyed()) {
2920 if (battle.getSourceFleet(curr) == null) continue;
2921 if (!fleetsWithDecks.contains(battle.getSourceFleet(curr))) continue;
2922 if (curr.isFighterWing()) {
2923 saved.add(curr);
2924 }
2925 }
2926
2927 result.getDestroyed().removeAll(saved);
2928 result.getRetreated().addAll(saved);
2929
2930
2931 List<FleetMemberAPI> toRepair = new ArrayList<FleetMemberAPI>();
2932 toRepair.addAll(result.getDeployed());
2933 toRepair.addAll(result.getRetreated());
2934 for (FleetMemberAPI curr : toRepair) {
2935 if (battle.getSourceFleet(curr) == null) continue;
2936 if (curr.isFighterWing()) {
2937 if (fleetsWithDecks.contains(battle.getSourceFleet(curr))) {
2938 curr.getStatus().repairFully();
2939 } else {
2940 curr.getStatus().repairFullyNoNewFighters();
2941 }
2942 }
2943 }
2944
2945 }
2946
2947 protected void applyCrewLosses(EngagementResultAPI result) {
2950
2951 //boolean playerInvolved = winner.getFleet().isPlayerFleet() || loser.getFleet().isPlayerFleet();
2952 boolean playerInvolved = battle.isPlayerInvolved();
2953 calculateAndApplyCrewLosses(winner, playerInvolved);
2954 calculateAndApplyCrewLosses(loser, playerInvolved);
2955
2956// applyCrewLosses(winner);
2957// applyCrewLosses(loser);
2958 }
2959
2960 protected void applyShipLosses(EngagementResultAPI result) {
2963
2964 applyShipLosses(winner);
2965 applyShipLosses(loser);
2966
2967 applyCREffect(winner);
2968 applyCREffect(loser);
2969 }
2970
2971 protected Map<FleetMemberAPI, Float> preEngagementCRForWinner = new HashMap<FleetMemberAPI, Float>();
2973 boolean wonBattle = result.isWinner();
2974 if (wonBattle) {
2976 for (FleetMemberAPI member : result.getFleet().getFleetData().getMembersListCopy()) {
2977 preEngagementCRForWinner.put(member, member.getRepairTracker().getCR());
2978 }
2979 }
2980
2981 List<FleetMemberAPI> applyDeployCostTo = new ArrayList<FleetMemberAPI>(result.getDeployed());
2982
2983 for (FleetMemberAPI member : result.getDisabled()) {
2984 // does not work, needs more things changed to work
2985 //float mult = member.getStats().getDynamic().getValue(Stats.CR_LOSS_WHEN_DISABLED_MULT);
2986 float mult = 1f;
2987 if (mult > 0) {
2988 member.getRepairTracker().applyCREvent(-1f * mult, "disabled in combat");
2989 }
2990 if (mult < 1) {
2991 applyDeployCostTo.add(member);
2992 }
2993 }
2994 for (FleetMemberAPI member : result.getDestroyed()) {
2995// if (member.getHullId().equals("vanguard_pirates")) {
2996// System.out.println("efwefwef");
2997// }
2998 //float mult = member.getStats().getDynamic().getValue(Stats.CR_LOSS_WHEN_DISABLED_MULT);
2999 float mult = 1f;
3000 if (mult > 0) {
3001 member.getRepairTracker().applyCREvent(-1f * mult, "disabled in combat");
3002 }
3003 if (mult < 1) {
3004 applyDeployCostTo.add(member);
3005 }
3006 }
3007
3008 for (FleetMemberAPI member : applyDeployCostTo) {
3009 float deployCost = getDeployCost(member);
3010 if (member.isFighterWing()) {
3011 member.getRepairTracker().applyCREvent(-deployCost, "wing deployed in combat");
3012 } else {
3013 member.getRepairTracker().applyCREvent(-deployCost, "deployed in combat");
3014 }
3015
3016 applyExtendedCRLossIfNeeded(result, member);
3017 }
3018
3019 //float retreatLossMult = StarfarerSettings.getCRLossMultForRetreatInLoss();
3020 float retreatLossMult = Global.getSettings().getFloat("crLossMultForRetreatInLoss");
3021 for (FleetMemberAPI member : result.getRetreated()) {
3022 float deployCost = getDeployCost(member);
3023 if (member.isFighterWing()) {
3024 member.getRepairTracker().applyCREvent(-deployCost, "wing deployed in combat");
3025 } else {
3026 member.getRepairTracker().applyCREvent(-deployCost, "deployed in combat");
3027 }
3028
3029 applyExtendedCRLossIfNeeded(result, member);
3030
3031 if (!wonBattle && result.getGoal() != FleetGoal.ESCAPE) {
3032 float retreatCost = deployCost * retreatLossMult;
3033 if (retreatCost > 0) {
3034 member.getRepairTracker().applyCREvent(-retreatCost, "retreated from lost engagement");
3035 }
3036 }
3037 }
3038
3039// // important, so that in-combat Ship objects can be garbage collected.
3040// // Probably some combat engine references in there, too.
3041// // NOTE: moved this elsewhere in this class
3042// result.resetAllEverDeployed();
3043// getDataFor(result.getFleet()).getMemberToDeployedMap().clear();
3044 }
3045
3046
3047// protected void saveAmmoState(FleetMemberAPI member, ShipAPI ship) {
3048// if (ship == null) return;
3049//
3050// Map<String, Integer> ammo = member.getAmmoStateAtEndOfLastEngagement();
3051// for (WeaponAPI w : ship.getAllWeapons()) {
3052// if (w.usesAmmo() && w.getAmmoPerSecond() <= 0) {
3053// ammo.put(w.getSlot().getId(), w.getAmmo());
3054// }
3055// }
3056// }
3057
3063 DeployedFleetMemberAPI dfm = getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
3064 if (dfm == null) return;
3065
3066 if (battle != null && battle.getSourceFleet(member) == null) {
3067 return;
3068 }
3069 if (member.getFleetCommander() == null) {
3070 return;
3071 }
3072
3073 if (dfm.getMember() == member && dfm.isFighterWing()) {
3074 //float finalCR = dfm.getShip().getRemainingWingCR();
3075 float cr = member.getRepairTracker().getBaseCR();
3076 float finalCR = cr;
3077 if (cr > finalCR) {
3078 member.getRepairTracker().applyCREvent(-(cr - finalCR), "deployed replacement chassis in combat");
3079 }
3080 return;
3081 }
3082 if (dfm.getMember() == member && !dfm.isFighterWing()) {
3083 float deployCost = getDeployCost(member);
3084 float endOfCombatCR = dfm.getShip().getCurrentCR() - deployCost;
3085 float cr = member.getRepairTracker().getCR();
3086 if (cr > endOfCombatCR) {
3087 member.getRepairTracker().applyCREvent(-(cr - endOfCombatCR), "extended deployment");
3088 }
3089
3090 ShipAPI ship = dfm.getShip();
3091 if (dfm.getShip() != null && !dfm.isFighterWing()) {
3092 float wMult = Global.getSettings().getFloat("crLossMultForWeaponDisabled");
3093 float eMult = Global.getSettings().getFloat("crLossMultForFlameout");
3094 float mMult = Global.getSettings().getFloat("crLossMultForMissilesFired");
3095 float hMult = Global.getSettings().getFloat("crLossMultForHullDamage");
3096
3097 float hullDamageFraction = ship.getHullLevelAtDeployment() - ship.getLowestHullLevelReached();
3098 float hullDamageCRLoss = hullDamageFraction * hMult;
3099 hullDamageCRLoss *= ship.getMutableStats().getDynamic().getValue(Stats.HULL_DAMAGE_CR_LOSS);
3100 if (hullDamageCRLoss > 0) {
3101 member.getRepairTracker().applyCREvent(-hullDamageCRLoss, "hull damage sustained");
3102 }
3103
3105
3106
3107 float instaRepairFraction = member.getStats().getDynamic().getValue(Stats.INSTA_REPAIR_FRACTION, 0f);
3108 if (instaRepairFraction > 0) {
3109 float hullDamage = member.getStatus().getHullDamageTaken();
3110 float armorDamage = member.getStatus().getArmorDamageTaken();
3111
3112 member.getStatus().repairArmorAllCells(armorDamage * instaRepairFraction);
3113 member.getStatus().repairHullFraction(hullDamage * instaRepairFraction);
3114 }
3115
3116
3117 float totalDisabled = 0f;
3119 float maxOP = ship.getVariant().getHullSpec().getOrdnancePoints(stats);
3120 if (maxOP <= 1) maxOP = 1;
3121
3122 for (WeaponAPI w : ship.getDisabledWeapons()) {
3123 totalDisabled += w.getSpec().getOrdnancePointCost(stats, ship.getVariant().getStatsForOpCosts()) * wMult;
3124 }
3125 if (ship.getNumFlameouts() > 0) {
3126 totalDisabled += maxOP * eMult;
3127 }
3128
3129 float damageBasedCRLoss = Math.min(1f, totalDisabled / maxOP);
3130 if (damageBasedCRLoss > 0) {
3131 member.getRepairTracker().applyCREvent(-damageBasedCRLoss, "weapon and engine damage sustained");
3132 }
3133
3134 float missileReloadOP = 0f;
3135 for (WeaponAPI w : ship.getAllWeapons()) {
3136 if (w.getType() == WeaponType.MISSILE && w.usesAmmo()) {
3137 missileReloadOP += (1f - (float) w.getAmmo() / (float) w.getMaxAmmo()) * w.getSpec().getOrdnancePointCost(stats, ship.getVariant().getStatsForOpCosts()) * mMult;
3138 }
3139 }
3140
3141 float missileReloadLoss = Math.min(1f, missileReloadOP / maxOP);
3142 if (missileReloadLoss > 0) {
3143 member.getRepairTracker().applyCREvent(-missileReloadLoss, "missile weapons used in combat");
3144 }
3145 }
3146
3147 return;
3148 }
3149 }
3150
3151
3153 for (FleetMemberAPI member : result.getDestroyed()) {
3154 if (battle.getSourceFleet(member) == null) continue;
3156 result.getFleet().getFleetData().removeFleetMember(member);
3157 }
3158 for (FleetMemberAPI member : result.getDisabled()) {
3159 if (battle.getSourceFleet(member) == null) continue;
3161 result.getFleet().getFleetData().removeFleetMember(member);
3162 }
3163 }
3164
3165// protected void applyCrewLosses(EngagementResultForFleetAPI result) {
3166// CargoAPI cargo = result.getFleet().getCargo();
3167// DataForEncounterSide data = getDataFor(result.getFleet());
3168// CrewCompositionAPI crewLosses = data.getCrewLossesDuringLastEngagement();
3169//
3170// cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, CargoAPI.CrewXPLevel.GREEN.getId(), crewLosses.getGreen());
3171// cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, CargoAPI.CrewXPLevel.REGULAR.getId(), crewLosses.getRegular());
3172// cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, CargoAPI.CrewXPLevel.VETERAN.getId(), crewLosses.getVeteran());
3173// cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, CargoAPI.CrewXPLevel.ELITE.getId(), crewLosses.getElite());
3174//
3175// cargo.removeMarines((int) crewLosses.getMarines());
3176// }
3177
3178 protected float computeLossFraction(FleetMemberAPI member, EngagementResultForFleetAPI result, float hullFraction, float hullDamage) {
3179 if (member == null && hullFraction == 0) {
3180 return (0.75f + (float) Math.random() * 0.25f);
3181 }
3182
3183 //System.out.println("hullDamage: " + hullDamage);
3184 if (member.isFighterWing() && result != null) {
3185 //System.out.println("Fighter hullDamage: " + hullDamage);
3186 float extraLossMult = hullDamage;
3187 DeployedFleetMemberAPI dfm = getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
3188 if (dfm != null && dfm.getMember() == member) {
3189 //float finalCR = dfm.getShip().getRemainingWingCR();
3190 float cr = member.getRepairTracker().getCR();
3191 float finalCR = cr;
3192 if (cr > finalCR) {
3194 float extraCraftLost = (cr - finalCR) / crPer;
3195 float wingSize = dfm.getMember().getNumFightersInWing();
3196 if (extraCraftLost >= 1) {
3197 extraLossMult = hullDamage + extraCraftLost / wingSize;
3198 }
3199 }
3200 }
3201 return (0.25f + (float) Math.random() * 0.75f * (float) Math.random()) * member.getStats().getCrewLossMult().getModifiedValue() * extraLossMult;
3202 }
3203
3204
3205 float extraFromFighters = 0f;
3206 if (!member.isFighterWing() && result != null) {
3207 DeployedFleetMemberAPI dfm = getDataFor(result.getFleet()).getMemberToDeployedMap().get(member);
3208 if (dfm != null && dfm.getMember() == member) {
3209 float craftCrewLoss = 0;
3210 for (FighterLaunchBayAPI bay : dfm.getShip().getLaunchBaysCopy()) {
3211 if (bay.getWing() == null || bay.getWing().getLeader() == null) continue;
3212 float baseCrew = bay.getWing().getLeader().getHullSpec().getMinCrew();
3213 float perCraft = bay.getWing().getLeader().getMutableStats().getMinCrewMod().computeEffective(baseCrew);
3214 perCraft *= bay.getWing().getLeader().getMutableStats().getDynamic().getValue(Stats.FIGHTER_CREW_LOSS_MULT);
3215 craftCrewLoss += perCraft * bay.getNumLost();
3216 }
3217
3218 float baseLossFraction = Global.getSettings().getFloat("fighterCrewLossBase");
3219 craftCrewLoss *= baseLossFraction;
3220
3221 float memberCrew = member.getMinCrew();
3222 if (memberCrew > 0) {
3223 float threshold = memberCrew * 0.33f;
3224
3225 float actualLost = 0f;
3226 float mult = 1f;
3227 do {
3228 float curr = Math.min(craftCrewLoss, threshold);
3229 craftCrewLoss -= curr;
3230
3231 curr *= mult;
3232 actualLost += curr;
3233 mult /= 2f;
3234
3235 } while (craftCrewLoss > 0);
3236
3237 extraFromFighters = actualLost / memberCrew;
3238 extraFromFighters *= member.getStats().getDynamic().getValue(Stats.FIGHTER_CREW_LOSS_MULT);
3239 }
3240 }
3241 }
3242
3243 if (hullFraction == 0) {
3244 return Math.min(1f, (0.75f + (float) Math.random() * 0.25f) * member.getStats().getCrewLossMult().getModifiedValue() + extraFromFighters);
3245 }
3246 return Math.min(1f, hullDamage * hullDamage * (0.5f + (float) Math.random() * 0.5f) * member.getStats().getCrewLossMult().getModifiedValue() + extraFromFighters);
3247 }
3248
3249
3250
3251 protected float computeRecoverableFraction(FleetMemberAPI member, EngagementResultForFleetAPI result, float hullFraction, float hullDamage) {
3252 float f = 1f - computeLossFraction(member, result, hullFraction, hullDamage);
3253 if (f < 0) f = 0;
3254 return f;
3255 }
3256
3257 public void calculateAndApplyCrewLosses(EngagementResultForFleetAPI result, boolean playerInvolved) {
3258 boolean wonBattle = result.isWinner();
3259
3260 DataForEncounterSide data = getDataFor(result.getFleet());
3261 CrewCompositionAPI recoverable = data.getRecoverableCrewLosses();
3262 //recoverable.removeAllCrew();
3263
3264 List<FleetMemberAPI> all = new ArrayList<FleetMemberAPI>();
3265 all.addAll(result.getDisabled());
3266 all.addAll(result.getDeployed());
3267 all.addAll(result.getDestroyed());
3268 all.addAll(result.getRetreated());
3269 all.addAll(result.getReserves());
3270
3271 for (FleetMemberAPI member : result.getReserves()) {
3272 member.getStatus().resetDamageTaken();
3273 }
3274
3275 CrewCompositionAPI playerLosses = data.getCrewLossesDuringLastEngagement();
3276 playerLosses.removeAllCrew();
3277
3279 crewLosses.removeAllCrew();
3280
3281 CampaignFleetAPI playerFleet = null;
3282 //float maxExtraLoss = 0f;
3283 float playerCapacityLost = 0f;
3284 for (FleetMemberAPI member : all) {
3285 if (battle.getSourceFleet(member) == null) continue;
3286 boolean player = battle.getSourceFleet(member) != null && battle.getSourceFleet(member).isPlayerFleet();
3287 CrewCompositionAPI c = member.getCrewComposition();
3288 //float hull = member.getStatus().getHullFraction();
3289 float hullDamage = member.getStatus().getHullDamageTaken();
3290 float hullFraction = member.getStatus().getHullFraction();
3291 member.getStatus().resetDamageTaken();
3292
3293 //if (hullDamage > 0 && !result.getFleet().isPlayerFleet() && playerInvolved) {
3294// if (hullDamage > 0 && playerInvolved &&
3295// !battle.getPlayerSide().contains(battle.getSourceFleet(member))) {
3296// playerDidSeriousDamage = true;
3297// }
3298// if (lostBattle) {
3299// System.out.println("HERE");
3300// }
3301
3302 float f1 = computeLossFraction(member, result, hullFraction, hullDamage);
3303
3304 // ship is disabled or destroyed, lose all crew for now, but it may be recovered later
3305 if (result.getDisabled().contains(member) || result.getDestroyed().contains(member)) {
3306 if (playerInvolved &&
3307 !battle.getPlayerSide().contains(battle.getSourceFleet(member))) {
3309 }
3310 if (player) {
3311 if (f1 < 1) {
3312 recoverable.addCrew((1f - f1) * c.getCrew());
3313 }
3314 playerLosses.addCrew(c.getCrew() * 1f);
3315 playerCapacityLost += member.getMaxCrew();
3316 }
3317
3318 crewLosses.addCrew(c.getCrew() * 1f);
3319 c.setCrew(0);
3320 //c.addCrew(-c.getCrew() * f1);
3321 // c should now be left with the appropriate crew composition (base minus losses) to use
3322 // as a starting point for boarding actions
3323
3324 } else {
3325 // the ship is still ok, only lose the non-recoverable casualties
3326 // for fighters, which can lose more than their actual max crew
3327 if (f1 > 1) {
3328 crewLosses.addCrew((f1 - 1) * c.getCrew());
3329 }
3330
3331 float lost = c.getCrew() * f1;
3332 // both fighters and normal ships
3333 c.transfer(lost, crewLosses);
3334
3335 if (player) {
3336 playerLosses.addCrew(lost);
3337 playerCapacityLost += member.getMaxCrew() * f1;
3338 }
3339 }
3340
3341 if (battle.getSourceFleet(member).isPlayerFleet() &&
3342 (crewLosses.getCrew() > 0 || crewLosses.getMarines() > 0)) {
3343 playerFleet = battle.getSourceFleet(member);
3344 }
3345
3346 CargoAPI cargo = battle.getSourceFleet(member).getCargo();
3347 cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, (int)crewLosses.getCrew());
3348 cargo.removeMarines((int) crewLosses.getMarines());
3349 crewLosses.clear();
3350 }
3351
3352 // lose over-capacity crew
3353 if (playerFleet != null) {
3354 playerFleet.getFleetData().updateCargoCapacities();
3355 CargoAPI cargo = playerFleet.getCargo();
3356 float maxCrew = cargo.getMaxPersonnel();
3357 float totalCrew = cargo.getTotalCrew();
3358 float marines = cargo.getMarines();
3359 float recoverableTotal = recoverable.getCrew() + recoverable.getMarines();
3360
3361 //recoverableTotal = 0f; // uncomment to let recoverable crew not be lost due to being over capacity
3362
3363 float total = totalCrew + marines + recoverableTotal;
3364 if (maxCrew + playerCapacityLost > 0) {
3365 total *= playerCapacityLost / (maxCrew + playerCapacityLost);
3366 }
3367// if (!wonBattle) {
3368// total = totalCrew + marines;
3369// recoverableTotal = 0f;
3370// }
3371 //if (total > playerOvercapLosses) total = playerOvercapLosses;
3372 if (total > maxCrew) {
3373 //float toLose = Math.min(maxExtraLoss, total - maxCrew);
3374 float toLose = total - maxCrew;
3375 if (toLose > 0) {
3376 //recoverable.clear();
3377 recoverable.transfer(Math.min(recoverableTotal, toLose), null);
3378 toLose -= recoverableTotal;
3379 total -= recoverableTotal;
3380
3381 if (toLose > 0 && total > 0) {
3382 float crew = cargo.getCrew(); // this is 99% the same as totalCrew, but leaving as is for now
3383
3384 crewLosses.clear();
3385 crewLosses.addCrew((int)Math.ceil(crew / total * toLose));
3386 crewLosses.addMarines((int)Math.ceil(marines / total * toLose));
3387
3388 playerLosses.addCrew(crewLosses.getCrew() * 1f);
3389 playerLosses.addMarines(crewLosses.getMarines() * 1f);
3390
3391 cargo.removeItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, (int)crewLosses.getCrew());
3392 cargo.removeMarines((int) crewLosses.getMarines());
3393 crewLosses.clear();
3394 }
3395 }
3396 }
3397 }
3398
3399 }
3400
3401 public void recoverCrew(CampaignFleetAPI fleet) {
3402 if (battle.isPlayerSide(battle.getSideFor(fleet))) {
3403 DataForEncounterSide data = getDataFor(fleet);
3405 CrewCompositionAPI rec = data.getRecoverableCrewLosses();
3406
3407 cargo.addItems(CargoAPI.CargoItemType.RESOURCES, Commodities.CREW, rec.getCrew());
3408
3409 cargo.addMarines((int) rec.getMarines());
3410 }
3411 }
3412
3413
3414
3415 public float getDifficulty() {
3416 return difficulty;
3417 }
3418
3419 public void setDifficulty(float difficulty) {
3420 this.difficulty = difficulty;
3421 }
3422
3423 public boolean isComputedDifficulty() {
3424 return computedDifficulty;
3425 }
3426
3428 this.computedDifficulty = computedDifficulty;
3429 }
3430
3431 public static float MAX_XP_MULT = 6f;
3432
3433 protected float difficulty = 1f;
3434 protected boolean computedDifficulty = false;
3436 if (computedDifficulty) return difficulty;
3437
3438 computedDifficulty = true;
3439 if (battle == null || !battle.isPlayerInvolved()) {
3440 difficulty = 1f;
3441 return difficulty;
3442 }
3443
3444 float scorePlayer = 0f;
3445 float scoreEnemy = 0f;
3446
3447 float officerBase = 30;
3448 float officerPerLevel = 15;
3449 //float baseMult = 0.2f;
3450 float baseMult = 2f;
3451 float dModMult = 0.9f;
3452
3454 if (member.isMothballed()) continue;
3455 float mult = baseMult;
3456 if (member.isStation()) mult *= 1f;
3457 else if (member.isCivilian()) mult *= 0.25f;
3458 if (member.getCaptain() != null && !member.getCaptain().isDefault()) {
3459 scoreEnemy += officerBase + officerPerLevel * Math.max(1f, member.getCaptain().getStats().getLevel());
3460 }
3461 int dMods = DModManager.getNumDMods(member.getVariant());
3462 for (int i = 0; i < dMods; i++) {
3463 mult *= dModMult;
3464 }
3465// String prefix = "battle_difficulty_mult_";
3466// for (String tag : member.getHullSpec().getTags()) {
3467// if (tag.startsWith(prefix)) {
3468// tag = tag.replaceFirst(prefix, "");
3469// mult *= Float.parseFloat(tag);
3470// break;
3471// }
3472// }
3473 //scoreEnemy += member.getUnmodifiedDeploymentPointsCost() * mult;
3474 scoreEnemy += member.getFleetPointCost() * mult;
3475 }
3476 scoreEnemy *= 0.6f;
3477
3478 float maxPlayserShipScore = 0f;
3479
3480 officerBase *= 0.5f;
3481 officerPerLevel *= 0.5f;
3482 Set<PersonAPI> seenOfficers = new HashSet<PersonAPI>();
3483 int unofficeredShips = 0;
3485 if (member.isMothballed()) continue;
3486 float mult = baseMult;
3487 if (member.isStation()) mult *= 1f;
3488 else if (member.isCivilian()) mult *= 0.25f;
3489 if (member.getCaptain() != null && !member.getCaptain().isDefault()) {
3490 scorePlayer += officerBase + officerPerLevel * Math.max(1f, member.getCaptain().getStats().getLevel());
3491 seenOfficers.add(member.getCaptain());
3492 } else if (!member.isCivilian()) {
3493 unofficeredShips++;
3494 }
3495 int dMods = DModManager.getNumDMods(member.getVariant());
3496 for (int i = 0; i < dMods; i++) {
3497 mult *= dModMult;
3498 }
3499 //float currShipBaseScore = member.getUnmodifiedDeploymentPointsCost() * mult;
3500 float currShipBaseScore = member.getFleetPointCost() * mult;
3501 scorePlayer += currShipBaseScore;
3502 if (battle.getSourceFleet(member) != null && battle.getSourceFleet(member).isPlayerFleet()) {
3503 maxPlayserShipScore = Math.max(maxPlayserShipScore, currShipBaseScore);
3504 }
3505 }
3506
3507 // so that removing officers from ships prior to a fight doesn't increase the XP gained
3508 // otherwise would usually be optimal to do this prior to every fight for any officers
3509 // on ships that aren't expected to be deployed
3511 if (seenOfficers.contains(od.getPerson())) continue;
3512 if (od.getPerson().isPlayer()) continue;
3513 if (unofficeredShips <= 0) break;
3514 unofficeredShips--;
3515 scorePlayer += officerBase + officerPerLevel * Math.max(1f, od.getPerson().getStats().getLevel());
3516 }
3517
3518 scorePlayer = Math.max(scorePlayer, Math.min(scoreEnemy * 0.5f, maxPlayserShipScore * 6f));
3519
3520 if (scorePlayer < 1) scorePlayer = 1;
3521 if (scoreEnemy < 1) scoreEnemy = 1;
3522
3523
3524// difficulty = scoreEnemy / (scorePlayer + scoreEnemy);
3525// if (difficulty > 1) difficulty = 1;
3526// if (scorePlayer < scoreEnemy) {
3527// difficulty *= MAX_XP_MULT;
3528// }
3529 //difficulty = scoreEnemy / (1f * scorePlayer);
3530 difficulty = scoreEnemy / scorePlayer;
3531 if (difficulty < 0) difficulty = 0;
3533 return difficulty;
3534 }
3535}
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
static SettingsAPI getSettings()
Definition Global.java:57
static FactoryAPI getFactory()
Definition Global.java:41
static SectorAPI getSector()
Definition Global.java:65
Map< FleetMemberAPI, DealtByFleetMember > getDealt()
static void reportExtraSalvageShown(SectorEntityToken entity)
float computeEffective(float baseValue)
static void addDMods(FleetMemberData data, boolean own, CampaignFleetAPI recoverer, Random random)
static boolean setDHull(ShipVariantAPI variant)
static int getNumDMods(ShipVariantAPI variant)
List< FleetMemberAPI > getRecoverableShips(BattleAPI battle, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet)
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 tallyOfficerTime(DataForEncounterSide data, EngagementResultForFleetAPI result)
void lootHullMods(FleetMemberAPI member, ShipVariantAPI variant, float mult)
boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText)
Map< FleetMemberAPI, CampaignFleetAPI > origSourceForRecoveredShips
void applyPursuitOption(CampaignFleetAPI pursuingFleet, CampaignFleetAPI otherFleet, PursuitOption pursuitOption)
void gainXP(DataForEncounterSide side, DataForEncounterSide otherSide)
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)
float getBoardingSuccessPercent(FleetMemberAPI member, CampaignFleetAPI attacker, CampaignFleetAPI defender)
static void prepareShipForRecovery(FleetMemberAPI member, boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods, float weaponRetainProb, float wingRetainProb, Random salvageRandom)
DataForEncounterSide getDataFor(CampaignFleetAPI participantOrCombined)
static void recoverShips(List< FleetMemberAPI > ships, FleetEncounterContext context, CampaignFleetAPI winningFleet, CampaignFleetAPI otherFleet)
void computeMissedLaunchLosses(BoardingResult result, CrewCompositionAPI boardingParty)
PursueAvailability getPursuitAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet)
void clearNoSourceMembers(EngagementResultForFleetAPI result)
void computeCrewLossFromBoarding(BoardingResult result, FleetMemberAPI member, CrewCompositionAPI boardingParty, float attackerStr, float defenderStr)
void handleCargoLooting(List< FleetMemberAPI > recoveredShips, boolean takingFromPlayer)
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)
EngageBoardableOutcome engageBoardableShip(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet)
void setPlayerFPHullDamageToEnemies(float playerFPHullDamageToEnemies)
boolean adjustPlayerReputation(InteractionDialogAPI dialog, String ffText, boolean okToAdjustAlly, boolean okToAdjustEnemy)
DisengageHarryAvailability getDisengageHarryAvailability(CampaignFleetAPI fleet, CampaignFleetAPI otherFleet)
void generatePlayerLoot(List< FleetMemberAPI > recoveredShips, boolean withCredits)
void calculateAndApplyCrewLosses(EngagementResultForFleetAPI result, boolean playerInvolved)
void lootWeapons(FleetMemberAPI member, ShipVariantAPI variant, boolean own, float mult, boolean lootingModule)
void letBoardableGo(FleetMemberAPI toBoard, CampaignFleetAPI fleetItBelongsTo, CampaignFleetAPI attackingFleet)
float computeRecoverableFraction(FleetMemberAPI member, EngagementResultForFleetAPI result, float hullFraction, float hullDamage)
static float getAdjustedGantryModifierForPostCombatSalvage(CampaignFleetAPI fleet)
static final String ENEMY_WING_RECOVERY_MOD
Definition Stats.java:98
static final String INDIVIDUAL_SHIP_RECOVERY_MOD
Definition Stats.java:200
static final String FUEL_SALVAGE_VALUE_MULT_FLEET
Definition Stats.java:81
static final String OWN_WEAPON_RECOVERY_MOD
Definition Stats.java:94
static final String BATTLE_SALVAGE_MULT_FLEET
Definition Stats.java:84
static final String ENEMY_WEAPON_RECOVERY_MOD
Definition Stats.java:97
static final String VARIANT_DO_NOT_DROP_AI_CORE_FROM_CAPTAIN
Definition Tags.java:77
static final String VARIANT_ALWAYS_RETAIN_SMODS_ON_SALVAGE
Definition Tags.java:75
static final String VARIANT_ALWAYS_RECOVERABLE
Definition Tags.java:78
static final String TAG_RETAIN_SMODS_ON_RECOVERY
Definition Tags.java:14
static final String VARIANT_CONSISTENT_WEAPON_DROPS
Definition Tags.java:76
static CargoAPI generateSalvage(Random random, float valueMult, float overallMult, float fuelMult, List< DropData > dropValue, List< DropData > dropRandom)
static CargoAPI getCombinedExtraSalvage(Map< String, MemoryAPI > memoryMap)
static int getMaxOfficers(CampaignFleetAPI fleet)
Definition Misc.java:5219
static Random getRandom(long seed, int level)
Definition Misc.java:2973
static float[] getBonusXPForScuttling(FleetMemberAPI member)
Definition Misc.java:5775
static boolean isShipRecoverable(FleetMemberAPI member, CampaignFleetAPI recoverer, boolean own, boolean useOfficerRecovery, float chanceMult)
Definition Misc.java:3317
static void forgetAboutTransponder(CampaignFleetAPI fleet)
Definition Misc.java:4168
static boolean isPlayerOrCombinedContainingPlayer(CampaignFleetAPI fleet)
Definition Misc.java:2250
static boolean isUnremovable(PersonAPI person)
Definition Misc.java:5473
static int getNumNonMercOfficers(CampaignFleetAPI fleet)
Definition Misc.java:5223
static boolean isPlayerOrCombinedPlayerPrimary(CampaignFleetAPI fleet)
Definition Misc.java:2240
static boolean isUnboardable(FleetMemberAPI member)
Definition Misc.java:3293
static void makeLowRepImpact(CampaignFleetAPI fleet, String reason)
Definition Misc.java:1465
static boolean isEasy()
Definition Misc.java:2284
CargoAPI createCargo(boolean unlimitedStacks)
CrewCompositionAPI createCrewComposition()
HullModSpecAPI getHullModSpec(String modId)
CommoditySpecAPI getCommoditySpec(String commodityId)
FighterWingSpecAPI getFighterWingSpec(String wingId)
WeaponSpecAPI getWeaponSpec(String weaponId)
List< CampaignFleetAPI > getPlayerSideSnapshot()
List< CampaignFleetAPI > getSnapshotSideFor(CampaignFleetAPI participantOrCombined)
CampaignFleetAPI getCombinedFor(CampaignFleetAPI participantOrCombined)
CampaignFleetAPI getPrimary(List< CampaignFleetAPI > side)
List< CampaignFleetAPI > getSideFor(CampaignFleetAPI participantOrCombined)
void setPlayerInvolvementFraction(float playerInvolvementFraction)
List< CampaignFleetAPI > getSnapshotFor(List< CampaignFleetAPI > side)
CampaignFleetAPI getSourceFleet(FleetMemberAPI member)
boolean knowsWhoPlayerIs(List< CampaignFleetAPI > side)
List< CampaignFleetAPI > getBothSides()
List< CampaignFleetAPI > getNonPlayerSide()
Map< FleetMemberAPI, CampaignFleetAPI > getMemberSourceMap()
boolean isPlayerSide(EngagementResultForFleetAPI side)
List< CampaignFleetAPI > getPlayerSide()
void removeFleetMemberWithDestructionFlash(FleetMemberAPI member)
MutableCharacterStatsAPI getCommanderStats()
void addHullmods(String id, int count)
void addFromStack(CargoStackAPI stack)
void addWeapons(String id, int count)
boolean removeItems(CargoAPI.CargoItemType itemType, Object data, float quantity)
List< CargoStackAPI > getStacksCopy()
void addItems(CargoAPI.CargoItemType itemType, Object data, float quantity)
void addCommodity(String commodityId, float quantity)
SpecialItemSpecAPI getSpecialItemSpecIfSpecial()
List< DeployedFleetMemberAPI > getAllEverDeployedCopy()
OfficerDataAPI getOfficerData(PersonAPI person)
ArrayList< FleetMemberAPI > getSnapshot()
List< OfficerDataAPI > getOfficersCopy()
void addFleetMember(FleetMemberAPI member)
void removeFleetMember(FleetMemberAPI member)
List< FleetMemberAPI > getCombatReadyMembersListCopy()
List< FleetMemberAPI > getMembersListCopy()
void reportPlayerEngagement(EngagementResultAPI result)
ReputationAdjustmentResult adjustPlayerReputation(Object action, String factionId)
void reportBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle)
MutableCharacterStatsAPI getPlayerStats()
void reportBattleFinished(CampaignFleetAPI primaryWinner, BattleAPI battle)
boolean isCurrentAssignment(FleetAssignment assignment)
void addAssignmentAtStart(FleetAssignment assignment, SectorEntityToken target, float maxDurationInDays, Script onCompletion)
CommoditySpecAPI getCommoditySpec(String commodityId)
void addXP(long xp, TextPanelAPI textPanel, boolean withMessage, boolean allowBonusXP, boolean withLevelUp)
void setOnlyAddBonusXPDoNotSpendStoryPoints(boolean onlyAddBonusXPDoNotSpendStoryPoints)
void spendStoryPoints(int points, boolean withMessage, TextPanelAPI textPanel, boolean topScreenMessage, String logText)
MutableCharacterStatsAPI getStats()
EngagementResultForFleetAPI getLoserResult()
EngagementResultForFleetAPI getWinnerResult()
Set< WeaponAPI > getDisabledWeapons()
MutableShipStatsAPI getMutableStats()
List< WeaponAPI > getAllWeapons()
List< FighterLaunchBayAPI > getLaunchBaysCopy()
int getOrdnancePoints(MutableCharacterStatsAPI stats)
LinkedHashSet< String > getSMods()
Map< String, String > getStationModules()
void setWingId(int index, String wingId)
ShipVariantAPI getModuleVariant(String slotId)
void setOriginalVariant(String targetVariant)
void setModuleVariant(String slotId, ShipVariantAPI variant)
void setSource(VariantSource source)
WeaponSlotAPI getSlot(String slotId)
void removeAll(CrewCompositionAPI other)
void addAll(CrewCompositionAPI other)
void transfer(float quantity, CrewCompositionAPI dest)
void setCaptain(PersonAPI commander)
void setVariant(ShipVariantAPI variant, boolean withRefit, boolean withStatsUpdate)
void setDetached(int index, Boolean detached)
void setMothballed(boolean mothballed)
void applyCREvent(float crChange, String description)