1package com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special;
3import java.util.ArrayList;
6import org.lwjgl.input.Keyboard;
8import com.fs.starfarer.api.Global;
9import com.fs.starfarer.api.campaign.CampaignFleetAPI;
10import com.fs.starfarer.api.campaign.CargoAPI;
11import com.fs.starfarer.api.campaign.CargoStackAPI;
12import com.fs.starfarer.api.campaign.FactionAPI;
13import com.fs.starfarer.api.campaign.FleetMemberPickerListener;
14import com.fs.starfarer.api.campaign.InteractionDialogAPI;
15import com.fs.starfarer.api.campaign.PlanetAPI;
16import com.fs.starfarer.api.campaign.SectorEntityToken;
17import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
18import com.fs.starfarer.api.combat.ShipVariantAPI;
19import com.fs.starfarer.api.fleet.FleetMemberAPI;
20import com.fs.starfarer.api.fleet.FleetMemberType;
21import com.fs.starfarer.api.impl.campaign.DModManager;
22import com.fs.starfarer.api.impl.campaign.FleetEncounterContext;
23import com.fs.starfarer.api.impl.campaign.ids.Commodities;
24import com.fs.starfarer.api.impl.campaign.ids.Entities;
25import com.fs.starfarer.api.impl.campaign.ids.Factions;
26import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
27import com.fs.starfarer.api.impl.campaign.ids.Stats;
28import com.fs.starfarer.api.impl.campaign.ids.Tags;
29import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity;
30import com.fs.starfarer.api.impl.campaign.rulecmd.FireAll;
31import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest;
32import com.fs.starfarer.api.impl.campaign.rulecmd.ShowDefaultVisual;
33import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageSpecialInteraction.SalvageSpecialData;
34import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageSpecialInteraction.SalvageSpecialDialogPlugin;
35import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageSpecialInteraction.SalvageSpecialPlugin;
36import com.fs.starfarer.api.loading.HullModSpecAPI;
37import com.fs.starfarer.api.util.Misc;
38import com.fs.starfarer.api.util.WeightedRandomPicker;
42 public static final String
RECOVER =
"recover";
43 public static final String
NOT_NOW =
"not_now";
48 public static enum ShipCondition {
56 public static class PerShipData
implements Cloneable {
57 public ShipCondition condition = ShipCondition.AVERAGE;
58 public String variantId =
null;
59 public ShipVariantAPI variant =
null;
60 public String shipName =
null;
61 public String fleetMemberId =
null;
62 public boolean addDmods =
true;
63 public boolean pruneWeapons =
true;
64 public Boolean nameAlwaysKnown =
null;
65 public float sModProb = 0f;
66 public PerShipData(String variantId, ShipCondition condition) {
67 this(variantId, condition, 0f);
69 public PerShipData(String variantId, ShipCondition condition,
float sModProb) {
70 this(variantId, condition, Factions.INDEPENDENT, sModProb);
72 public PerShipData(ShipVariantAPI variant, ShipCondition condition, String shipName, String factionIdForShipName,
float sModProb) {
73 this.variant = variant;
74 this.condition = condition;
76 if (shipName !=
null) {
77 this.shipName = shipName;
79 FactionAPI faction =
Global.
getSector().getFaction(factionIdForShipName);
80 this.shipName = faction.pickRandomShipName();
83 this.sModProb = sModProb;
86 public PerShipData(String variantId, ShipCondition condition, String factionIdForShipName,
float sModProb) {
87 this.variantId = variantId;
88 this.condition = condition;
90 FactionAPI faction =
Global.
getSector().getFaction(factionIdForShipName);
91 this.shipName = faction.pickRandomShipName();
93 this.sModProb = sModProb;
96 public ShipVariantAPI getVariant() {
97 ShipVariantAPI result = variant;
98 if (result ==
null && variantId !=
null) {
99 result = Global.getSettings().getVariant(variantId);
105 public PerShipData clone() {
106 try {
return (PerShipData) super.clone(); }
catch (CloneNotSupportedException e) {
return null; }
111 public static class ShipRecoverySpecialData
implements SalvageSpecialData {
112 public List<PerShipData> ships =
new ArrayList<PerShipData>();
113 public String desc =
null;
114 public Boolean storyPointRecovery =
null;
115 public Boolean notNowOptionExits =
null;
116 public Boolean noDescriptionText =
null;
118 public ShipRecoverySpecialData(String desc) {
122 public void addShip(PerShipData ship) {
125 public void addShip(String variantId, ShipCondition condition,
float sModProb) {
126 ships.add(
new PerShipData(variantId, condition, sModProb));
128 public void addShip(String variantId, ShipCondition condition, String factionIdForShipName,
float sModProb) {
129 ships.add(
new PerShipData(variantId, condition, factionIdForShipName, sModProb));
132 public SalvageSpecialPlugin createSpecialPlugin() {
137 protected ShipRecoverySpecialData
data;
139 public static ShipRecoverySpecialData
getSpecialData(SectorEntityToken
entity, String desc,
boolean create,
boolean replace) {
140 Object o = Misc.getSalvageSpecial(
entity);
141 ShipRecoverySpecialData
data =
null;
142 if (o instanceof ShipRecoverySpecialData) {
143 data = (ShipRecoverySpecialData) o;
146 if (
data ==
null && !create)
return null;
147 if (o !=
null &&
data ==
null && !replace)
return null;
150 data =
new ShipRecoverySpecialData(desc);
162 public void init(InteractionDialogAPI
dialog, Object specialData) {
163 super.init(
dialog, specialData);
165 data = (ShipRecoverySpecialData) specialData;
172 if (
data.ships.isEmpty()) {
179 protected List<FleetMemberAPI>
members =
new ArrayList<FleetMemberAPI>();
180 protected List<FleetMemberAPI>
recovered =
new ArrayList<FleetMemberAPI>();
184 for (PerShipData curr :
data.ships) {
194 for (FleetMemberAPI member :
members) {
195 recoverable.getFleetData().addFleetMember(member);
198 if (recoverable.getFleetData().getMembersListCopy().size() == 1) {
199 visual.showFleetMemberInfo(recoverable.getFleetData().getMembersListCopy().get(0),
true);
201 visual.showFleetInfo(
"Your fleet",
playerFleet,
"Recoverable ships", recoverable,
null,
true);
212 if (
data.notNowOptionExits !=
null &&
data.notNowOptionExits) {
214 options.setShortcut(
NOT_NOW, Keyboard.KEY_ESCAPE,
false,
false,
false,
true);
224 options.addOption(
"Take a look at the chief engineer's report and make a decision",
RECOVER);
225 options.addOption(
"\"I'll need to consider my options.\"",
NOT_NOW);
230 return data !=
null && data.storyPointRecovery !=
null &&
data.storyPointRecovery;
236 if (
data.noDescriptionText !=
null &&
data.noDescriptionText) {
240 boolean debris = Entities.DEBRIS_FIELD_SHARED.equals(
entity.getCustomEntityType());
241 boolean wreck = Entities.WRECK.equals(
entity.getCustomEntityType());
242 wreck |=
entity.hasTag(Tags.WRECK);
245 boolean withDesc = !debris && !wreck;
248 addText(
"Some time later, you hear back from your chief engineer, " +
249 "who mumbles about being a miracle worker to someone offscreen before " +
250 "noticing you've picked up the call.");
252 addText(
"\"Commander, looks like we might be able to pull this off, though I'll say you're not going to " +
253 "find what I'm going to do in any manual. And it wouldn't pass any inspection, but then again " +
254 "we're not in the Hegemony fleet. It'll fly, though.");
256 addText(
"\"Commander, looks like we might be able to pull this off, though I'll say you're not going to " +
257 "find what I'm going to do in any manual. And it wouldn't pass any inspection, but then again " +
258 "we're not in the Hegemony fleet. These ships can be made to fly again, though.");
267 String ships =
"several ships";
268 String they =
"they";
275 if (
data.storyPointRecovery !=
null &&
data.storyPointRecovery) {
276 addText(
"A crack engineering team sent to the wreck reports successfully preparing it " +
280 addText(
"Salvage crews boarding the wreck discover that many essential systems " +
281 "are undamaged and the ship could be restored to basic functionality.");
284 if (es !=
null && !es.cargo.isEmpty()) {
285 addText(
"There are also indications that it has some sort of cargo on board.");
290 addText(
"Close-range scans of the debris field reveal " + ships +
291 " that could be restored to basic functionality.");
292 }
else if (withDesc) {
293 String desc =
data.desc;
294 if (desc ==
null) desc =
"floating near";
297 if (
entity instanceof PlanetAPI) {
298 addText(
"Salvage crews report " + ships +
" " + desc +
". " +
299 "Closer inspection reveals " + they +
" could be restored to basic functionality.");
301 addText(
"Salvage crews report " + ships +
" " + desc +
" the $shortName. " +
302 "Closer inspection reveals " + they +
" could be restored to basic functionality.");
307 if (
members.size() > 0 && Misc.getCurrSpecialMods(
members.get(0).getVariant()) > 0) {
308 text.addPara(
"The crew chief also reports that the hull has undergone %s, which appear to have " +
309 "survived its present state.",
310 Misc.getStoryOptionColor(),
"special modifications");
315 addText(
"If not recovered, the ship will be scuttled, " +
316 "and any fitted weapons and fighter LPCs will be retrieved.");
319 addText(
"Any ships that aren't recovered will be scuttled, " +
320 "and any fitted weapons and fighter LPCs will be retrieved.");
324 protected FleetMemberAPI
first =
null;
326 if (shipData.variant ==
null && shipData.variantId ==
null) {
330 FleetMemberAPI member =
null;
331 if (shipData.variantId !=
null) {
338 if (Misc.isUnboardable(member)) {
347 if (shipData.fleetMemberId !=
null) {
348 member.setId(shipData.fleetMemberId);
351 if (
isNameKnown(shipData.condition) || (shipData.nameAlwaysKnown !=
null && shipData.nameAlwaysKnown)) {
352 member.setShipName(shipData.shipName);
354 member.setShipName(
"<name unknown>");
361 return condition == ShipCondition.PRISTINE || condition == ShipCondition.GOOD;
369 int reduction = (int)
playerFleet.getStats().getDynamic().getValue(Stats.SHIP_DMOD_REDUCTION, 0);
370 reduction =
random.nextInt(reduction + 1);
374 member.getStatus().setRandom(
random);
376 for (
int i = 0; i < hits; i++) {
377 member.getStatus().applyDamage(1000000f);
381 member.getRepairTracker().setCR(0f);
384 ShipVariantAPI variant = member.getVariant();
385 variant = variant.clone();
386 variant.setOriginalVariant(
null);
389 dmods = Math.max(0, dmods - dModsAlready);
391 if (dmods > 0 && shipData.addDmods) {
394 member.setVariant(variant,
false,
true);
396 if (dmods > 0 && shipData.addDmods) {
400 if (shipData.sModProb > 0 &&
random.nextFloat() < shipData.sModProb) {
402 float r =
random.nextFloat();
405 }
else if (r > 0.5f) {
409 WeightedRandomPicker<String> picker =
new WeightedRandomPicker<String>(
random);
410 for (String
id : variant.getHullMods()) {
412 if (spec.isHidden())
continue;
413 if (spec.isHiddenEverywhere())
continue;
414 if (spec.hasTag(Tags.HULLMOD_DMOD))
continue;
415 if (spec.hasTag(Tags.HULLMOD_NO_BUILD_IN))
continue;
416 if (variant.getPermaMods().contains(spec.getId()))
continue;
417 picker.add(
id, spec.getCapitalCost());
419 for (
int i = 0; i < num && !picker.isEmpty(); i++) {
420 String
id = picker.pickAndRemove();
421 variant.addPermaMod(
id,
true);
427 if (shipData.pruneWeapons) {
430 member.getVariant().autoGenerateWeaponGroups();
437 case PRISTINE:
return 1f;
438 case GOOD:
return 0.6f +
random.nextFloat() * 0.2f;
439 case AVERAGE:
return 0.4f +
random.nextFloat() * 0.2f;
440 case BATTERED:
return 0.2f +
random.nextFloat() * 0.2f;
441 case WRECKED:
return random.nextFloat() * 0.1f;
448 if (condition == ShipCondition.PRISTINE)
return 0;
452 case AVERAGE:
return 1 +
random.nextInt(2);
453 case BATTERED:
return 2 +
random.nextInt(2);
454 case WRECKED:
return 3 +
random.nextInt(2);
461 case PRISTINE:
return 1f;
462 case GOOD:
return 0.67f;
463 case AVERAGE:
return 0.5f;
464 case BATTERED:
return 0.33f;
465 case WRECKED:
return 0.2f;
471 if (condition == ShipCondition.PRISTINE)
return 0;
472 if (condition == ShipCondition.WRECKED)
return 20;
474 switch (member.getHullSpec().getHullSize()) {
477 case GOOD:
return 2 +
random.nextInt(2);
478 case AVERAGE:
return 4 +
random.nextInt(3);
479 case BATTERED:
return 7 +
random.nextInt(6);
484 case GOOD:
return 1 +
random.nextInt(2);
485 case AVERAGE:
return 2 +
random.nextInt(3);
486 case BATTERED:
return 4 +
random.nextInt(4);
491 case GOOD:
return 1 +
random.nextInt(2);
492 case AVERAGE:
return 2 +
random.nextInt(2);
493 case BATTERED:
return 3 +
random.nextInt(3);
499 case AVERAGE:
return 2;
500 case BATTERED:
return 3;
539 if (
RECOVER.equals(optionData)) {
546 if (
data.notNowOptionExits !=
null &&
data.notNowOptionExits) {
548 options.setShortcut(
NOT_NOW, Keyboard.KEY_ESCAPE,
false,
false,
false,
true);
554 List<FleetMemberAPI> pool =
members;
555 List<FleetMemberAPI> storyPool =
new ArrayList<FleetMemberAPI>();
561 dialog.showFleetMemberRecoveryDialog(
"Select ships to recover", pool, storyPool,
562 new FleetMemberPickerListener() {
563 public void pickedFleetMembers(List<FleetMemberAPI> selected) {
564 if (selected.isEmpty())
return;
568 for (FleetMemberAPI member : selected) {
569 int index =
members.indexOf(member);
571 PerShipData shipData =
data.ships.get(index);
572 data.ships.remove(index);
576 member.setShipName(shipData.shipName);
577 if (shipData.fleetMemberId !=
null) {
578 member.setId(shipData.fleetMemberId);
581 float minHull =
playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MIN, 0f);
582 float maxHull =
playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_HULL_MAX, 0f);
583 float minCR =
playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MIN, 0f);
584 float maxCR =
playerFleet.getStats().getDynamic().getValue(Stats.RECOVERED_CR_MAX, 0f);
586 float hull = (float) Math.random() * (maxHull - minHull) + minHull;
587 hull = Math.max(hull, member.getStatus().getHullFraction());
588 member.getStatus().setHullFraction(hull);
590 float cr = (float) Math.random() * (maxCR - minCR) + minCR;
591 member.getRepairTracker().setCR(cr);
601 if (
dialog.getPlugin() instanceof SalvageSpecialDialogPlugin) {
602 SalvageSpecialDialogPlugin plugin = (SalvageSpecialDialogPlugin)
dialog.getPlugin();
611 public void cancelledFleetMemberPicking() {
615 }
else if (
NOT_NOW.equals(optionData)) {
616 if (
data.notNowOptionExits !=
null &&
data.notNowOptionExits) {
624 Misc.setSalvageSpecial(
entity, Misc.getPrevSalvageSpecial(
entity));
642 boolean wreck = Entities.WRECK.equals(
entity.getCustomEntityType());
643 wreck |=
entity.hasTag(Tags.WRECK);
649 if (extra !=
null && !extra.isEmpty()) {
650 addText(
"Your crews find some securely stowed cargo during the recovery operation.");
656 for (CargoStackAPI stack : extra.getStacksCopy()) {
661 ListenerUtil.reportSpecialCargoGainedFromRecoveredDerelict(extra,
dialog);
665 addText(
"The " +
first.getShipName() +
" is now part of your fleet.");
674 for (FleetMemberAPI member :
recovered) {
675 dialog.getInteractionTarget().getMemoryWithoutUpdate().set(
"$srs_memberId", member.getId(), 0);
676 dialog.getInteractionTarget().getMemoryWithoutUpdate().set(
"$srs_hullId", member.getHullId(), 0);
677 dialog.getInteractionTarget().getMemoryWithoutUpdate().set(
"$srs_baseHullId", member.getHullSpec().getBaseHullId(), 0);
702 for (FleetMemberAPI member :
members) {
709 cargo.addCommodity(Commodities.SUPPLIES, member.getRepairTracker().getSuppliesFromScuttling());
710 cargo.addCommodity(Commodities.FUEL, member.getRepairTracker().getFuelFromScuttling());
711 cargo.addCommodity(Commodities.HEAVY_MACHINERY, member.getRepairTracker().getHeavyMachineryFromScuttling());
713 ShipVariantAPI variant = member.getVariant();
714 for (String slotId : variant.getNonBuiltInWeaponSlots()) {
715 cargo.addWeapons(variant.getWeaponId(slotId), 1);
719 for (String wingId : variant.getWings()) {
720 if (wingId !=
null && !wingId.isEmpty() && !variant.getHullSpec().isBuiltInWing(index)) {
721 cargo.addFighters(wingId, 1);
static SettingsAPI getSettings()
static FactoryAPI getFactory()
static SectorAPI getSector()
static void addDMods(FleetMemberData data, boolean own, CampaignFleetAPI recoverer, Random random)
static boolean setDHull(ShipVariantAPI variant)
static int getNumDMods(ShipVariantAPI variant)
static void prepareShipForRecovery(FleetMemberAPI member, boolean retainAllHullmods, boolean retainKnownHullmods, boolean clearSMods, float weaponRetainProb, float wingRetainProb, Random salvageRandom)
static void addStackGainText(CargoStackAPI stack, TextPanelAPI text)
static boolean fire(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
static boolean fire(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
boolean execute(String ruleId, InteractionDialogAPI dialog, List< Token > params, Map< String, MemoryAPI > memoryMap)
Map< String, MemoryAPI > memoryMap
void setDone(boolean done)
InteractionDialogAPI dialog
void addText(String format)
void addTempExtraSalvage(CargoAPI cargo)
static CargoAPI getCombinedExtraSalvage(Map< String, MemoryAPI > memoryMap)
void setShowAgain(boolean showAgain)
void setEndWithContinue(boolean endWithContinue)
CampaignFleetAPI playerFleet
static void clearExtraSalvage(Map< String, MemoryAPI > memoryMap)
void setShouldAbortSalvageAndRemoveEntity(boolean shouldAbortSalvageAndRemoveEntity)
static ExtraSalvage getExtraSalvage(SectorEntityToken entity)
List< FleetMemberAPI > recovered
static ShipRecoverySpecialData getSpecialData(SectorEntityToken entity, String desc, boolean create, boolean replace)
void optionSelected(String optionText, Object optionData)
static final String RECOVER
void addExtraSalvageFromUnrecoveredShips()
static boolean isNameKnown(ShipCondition condition)
float getFighterWeaponRetainProb(ShipCondition condition)
void prepareMember(FleetMemberAPI member, PerShipData shipData)
ShipRecoverySpecialData data
boolean isStoryPointRecovery()
static final String RECOVERY_FINISHED
static final String NOT_NOW
int getHitsForCondition(FleetMemberAPI member, ShipCondition condition)
void init(InteractionDialogAPI dialog, Object specialData)
int getDmodsForCondition(ShipCondition condition)
float getHullForCondition(ShipCondition condition)
void addMember(PerShipData shipData)
void addStuffFromMember(CargoAPI cargo, FleetMemberAPI member)
static final String ABORT_CONTINUE
List< FleetMemberAPI > members
CargoAPI createCargo(boolean unlimitedStacks)
CampaignFleetAPI createEmptyFleet(String factionId, String name, boolean aiMode)
FleetMemberAPI createFleetMember(FleetMemberType type, String variantOrWingId)
HullModSpecAPI getHullModSpec(String modId)