1package com.fs.starfarer.api.impl.campaign.rulecmd.salvage;
4import java.util.ArrayList;
5import java.util.LinkedHashMap;
8import java.util.Random;
10import org.lwjgl.input.Keyboard;
11import org.lwjgl.util.vector.Vector2f;
13import com.fs.starfarer.api.Global;
14import com.fs.starfarer.api.campaign.CampaignFleetAPI;
15import com.fs.starfarer.api.campaign.CargoAPI;
16import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
17import com.fs.starfarer.api.campaign.CargoStackAPI;
18import com.fs.starfarer.api.campaign.CoreInteractionListener;
19import com.fs.starfarer.api.campaign.FactionAPI;
20import com.fs.starfarer.api.campaign.InteractionDialogAPI;
21import com.fs.starfarer.api.campaign.OptionPanelAPI;
22import com.fs.starfarer.api.campaign.ResourceCostPanelAPI;
23import com.fs.starfarer.api.campaign.SectorEntityToken;
24import com.fs.starfarer.api.campaign.SpecialItemData;
25import com.fs.starfarer.api.campaign.TextPanelAPI;
26import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
27import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
28import com.fs.starfarer.api.campaign.rules.MemoryAPI;
29import com.fs.starfarer.api.combat.MutableStat;
30import com.fs.starfarer.api.combat.MutableStat.StatMod;
31import com.fs.starfarer.api.combat.ShipVariantAPI;
32import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin;
33import com.fs.starfarer.api.impl.campaign.RepairGantry;
34import com.fs.starfarer.api.impl.campaign.ids.Commodities;
35import com.fs.starfarer.api.impl.campaign.ids.Entities;
36import com.fs.starfarer.api.impl.campaign.ids.Items;
37import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
38import com.fs.starfarer.api.impl.campaign.ids.Stats;
39import com.fs.starfarer.api.impl.campaign.ids.Tags;
40import com.fs.starfarer.api.impl.campaign.procgen.DropGroupRow;
41import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec;
42import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData;
43import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
44import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageEntityGeneratorOld;
45import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin;
46import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest;
47import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BaseSalvageSpecial;
48import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipRecoverySpecialData;
49import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin;
50import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldParams;
51import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldSource;
52import com.fs.starfarer.api.ui.Alignment;
53import com.fs.starfarer.api.ui.TooltipMakerAPI;
54import com.fs.starfarer.api.ui.TooltipMakerAPI.StatModValueGetter;
55import com.fs.starfarer.api.util.Misc;
56import com.fs.starfarer.api.util.Misc.Token;
57import com.fs.starfarer.api.util.WeightedRandomPicker;
99 private Map<String, MemoryAPI> memoryMap;
105 this.memoryMap = memoryMap;
107 String command = params.get(0).getString(memoryMap);
108 if (command ==
null)
return false;
134 if (command.equals(
"showCost")) {
135 if (debris ==
null) {
141 }
else if (command.equals(
"performSalvage")) {
143 }
else if (command.equals(
"descDebris")) {
144 showDebrisDescription();
145 }
else if (command.equals(
"checkAccidents")) {
147 }
else if (command.equals(
"demolish")) {
149 }
else if (command.equals(
"canBeMadeRecoverable")) {
151 }
else if (command.equals(
"showRecoverable")) {
158 private void demolish() {
160 if (!isDebrisField) {
176 private float getAccidentProbability() {
177 if (debris ==
null)
return 0f;
178 float accidentProbability = 0.2f + 0.8f * (1f - debris.
getParams().density);
179 if (accidentProbability > 0.9f) accidentProbability = 0.9f;
180 return accidentProbability;
183 private void checkAccidents() {
184 if (debris ==
null) {
186 FireBest.fire(
null,
dialog, memoryMap,
"DialogOptionSelected");
190 float accidentProbability = getAccidentProbability();
194 Random random = Misc.getRandom(seed, 175);
196 if (random.nextFloat() > accidentProbability) {
198 FireBest.fire(
null,
dialog, memoryMap,
"DialogOptionSelected");
203 Color bad = Misc.getNegativeHighlightColor();
204 Color highlight = Misc.getHighlightColor();
207 float reqCrew = (int) requiredRes.get(Commodities.CREW);
208 float reqMachinery = (int) requiredRes.get(Commodities.HEAVY_MACHINERY);
212 float fCrew = crew / reqCrew;
213 if (fCrew < 0) fCrew = 0;
214 if (fCrew > 1) fCrew = 1;
216 float fMachinery = machinery / reqMachinery;
217 if (fMachinery < 0) fMachinery = 0;
218 if (fMachinery > 1) fMachinery = 1;
224 float lossValue = reqCrew * fCrew * 5f;
225 lossValue += (1f - debris.
getParams().density / debris.
getParams().baseDensity) * 500f;
226 lossValue *= 0.5f + random.nextFloat();
229 WeightedRandomPicker<String> lossPicker =
new WeightedRandomPicker<String>(random);
230 lossPicker.add(Commodities.CREW, 10f + 100f * (1f - fMachinery));
231 lossPicker.add(Commodities.HEAVY_MACHINERY, 10f + 100f * fMachinery);
233 CargoAPI losses = Global.getFactory().createCargo(
true);
235 while (loss < lossValue) {
236 String
id = lossPicker.pick();
237 CommoditySpecAPI
spec = Global.getSector().getEconomy().getCommoditySpec(
id);
238 loss +=
spec.getBasePrice();
239 losses.addCommodity(
id, 1f);
243 int crewLost = losses.getCrew();
245 losses.removeCrew(crewLost);
247 if (crewLost < 1) crewLost = 1;
248 losses.addCrew(crewLost);
251 int machineryLost = (int) losses.getCommodityQuantity(Commodities.HEAVY_MACHINERY);
252 if (crewLost > crew) crewLost = (int) crew;
253 if (machineryLost > machinery) machineryLost = (int) machinery;
255 if (crewLost <= 0 && machineryLost <= 0) {
257 FireBest.fire(
null,
dialog, memoryMap,
"DialogOptionSelected");
261 for (CargoStackAPI stack : losses.getStacksCopy()) {
268 text.
addParagraph(
"An accident during the operation has resulted in the loss of ");
273 }
else if (machineryLost <= 0) {
282 Global.getSoundPlayer().playSound(
"hit_solid", 1, 1, Global.getSoundPlayer().getListenerPos(),
new Vector2f());
290 private void showDebrisDescription() {
291 if (debris ==
null)
return;
294 if (daysLeft >= 1000) {
295 text.
addParagraph(
"The field appears stable and will not drift apart any time soon.");
297 String atLeastTime = Misc.getAtLeastStringForDays((
int) daysLeft);
298 text.
addParagraph(
"The field is unstable, but should not drift apart for " + atLeastTime +
".");
321 lootValue += data.value;
324 if (data.value > 0) {
325 lootValue += data.value;
342 if (lootValue < 500) {
345 }
else if (lootValue < 2500) {
353 float accidentProbability = getAccidentProbability();
354 if (accidentProbability <= 0.2f) {
356 text.
addPara(
"There are indications of some easy pickings to be had, and the risk of an accident during a salvage operation is low.",
357 Misc.getPositiveHighlightColor(),
"low");
358 }
else if (accidentProbability < 0.7f) {
359 text.
addPara(
"There are indications that what salvage is to be had may not be easy to get to, " +
360 "and there's %s risk involved in running a salvage operation.", Misc.getHighlightColor(),
"significant");
362 text.
addPara(
"The salvage that remains is extremely difficult to get to, " +
363 "and there's %s risk involved in running a salvage operation.", Misc.getNegativeHighlightColor(),
"high");
368 Map<String, Integer> result =
new LinkedHashMap<String, Integer>();
383 int crew = Math.round((
int) (
BASE_CREW * mult) / 10f) * 10;
384 int machinery = Math.round((
int) (
BASE_MACHINERY * mult) / 10f) * 10;
397 float machineryContrib = 0.75f;
399 if (machineryContrib < 1f) {
400 valueRecovery.
modifyPercent(
"base_positive", (
int) Math.round(100f - 100f * machineryContrib),
"Base effectiveness");
406 for (String commodityId : requiredRes.keySet()) {
407 float required = requiredRes.get(commodityId);
409 if (required <= 0)
continue;
412 float val = Math.min(available / required, 1f) * per;
413 int percent = (int) Math.round(val * 100f);
416 val = Math.min(available / required, machineryContrib) * per;
417 percent = (int) Math.round(val * 100f);
427 boolean modified =
false;
428 if (withSkillMultForRares) {
446 valueRecovery.
modifyPercentAlways(
"" + i++, (
int) Math.round(fleetSalvageShips * 100f),
"Fleetwide salvaging capability");
448 return valueRecovery;
471 text.
addParagraph(
"You receive a preliminary assessment of a potential salvage operation from the exploration crews.");
479 for (String commodityId : requiredRes.keySet()) {
480 int required = requiredRes.get(commodityId);
486 cost.
addCost(commodityId,
"" + required +
" (" + available +
")", curr);
494 int valuePercent = (int)Math.round(valueRecovery.
getModifiedValue() * 100f);
495 if (valuePercent < 0) valuePercent = 0;
496 String valueString =
"" + valuePercent +
"%";
497 Color valueColor = highlight;
499 if (valuePercent < 100) {
505 info.
addPara(
"Resource recovery effectiveness: %s", 0f, valueColor, valueString);
516 boolean percent =
false;
517 public String getPercentValue(StatMod mod) {
521 if (mod.desc ==
null || mod.desc.isEmpty())
return "";
523 String prefix = mod.getValue() >= 0 ?
"+" :
"";
524 return prefix + (int)(mod.getValue()) +
"%";
526 public String getMultValue(StatMod mod) {percent =
false;
return null;}
527 public String getFlatValue(StatMod mod) {percent =
false;
return null;}
528 public Color getModColor(StatMod mod) {
538 String fuelStr =
"" + (int)Math.round((fuelMult - 1f) * 100f) +
"%";
541 String rareStr =
"" + (int)Math.round((rareMult - 1f) * 100f) +
"%";
543 if (fuelMult > 1f && rareMult > 1f) {
544 text.
addPara(
"Your fleet also has a %s bonus to the amount of fuel recovered, and " +
545 "a %s bonus to the number of rare items found.",
547 }
else if (fuelMult > 1) {
548 text.
addPara(
"Your fleet also has a %s bonus to the amount of fuel recovered.",
550 }
else if (rareMult > 1) {
551 text.
addPara(
"Your fleet also has a %s bonus to the number of rare items found.",
555 if (debris !=
null) {
556 text.
addParagraph(
"The density of the debris field affects both the amount of resources and the number of rare items found.");
558 text.
addPara(
"The recovery effectiveness does not affect the chance of finding rare and valuable items.");
582 for (String commodityId : requiredRes.keySet()) {
583 int required = requiredRes.get(commodityId);
589 cost.
addCost(commodityId,
"" + required +
" (" + available +
")", curr);
596 valueRecovery.
modifyMult(
"debris_mult", overallMult,
"Debris field density");
598 int valuePercent = (int)Math.round(valueRecovery.
getModifiedValue() * 100f);
599 if (valuePercent < 0) valuePercent = 0;
600 String valueString =
"" + valuePercent +
"%";
601 Color valueColor = highlight;
603 if (valuePercent < 100) {
609 info.
addPara(
"Scavenging effectiveness: %s", 0f, valueColor, valueString);
625 float overallMult = 1f;
626 if (debris !=
null) {
640 DebrisFieldParams params = debris.
getParams();
641 if (params.baseDensity > 0) {
682 if (debris !=
null) {
689 CargoAPI salvage =
generateSalvage(random, valueMultFleet, rareItemSkillMult, overallMult, fuelMult, dropValue, dropRandom);
701 if (debris !=
null) {
702 debris.
getParams().density -= overallMult;
713 public void coreUIDismissed() {
746 String leave =
"Leave";
756 if (!isDebrisField) {
780 if (random ==
null) random =
new Random();
787 float debrisFieldRadius = 200f + salvageRating * 400f;
789 float density = 0.5f + salvageRating * 0.5f;
792 density = 0.5f + salvageRating * 0.5f;
795 float duration = 10f + salvageRating * 20f;
797 DebrisFieldParams params =
new DebrisFieldParams(debrisFieldRadius, density, duration, duration * 0.5f);
798 params.source = DebrisFieldSource.PLAYER_SALVAGE;
812 params.baseSalvageXP = (long) xp;
819 if (extra !=
null && !extra.
isEmpty()) {
859 DropData copy = data.clone();
860 copy.valueMult = valueMult;
864 DropData copy = data.clone();
865 copy.valueMult = valueMult;
870 DropData copy = data.clone();
871 copy.valueMult = valueMult;
875 DropData copy = data.clone();
876 copy.valueMult = valueMult;
900 return valueModShips;
915 float r = field.
getParams().bandWidthInEngine;
927 public static CargoAPI generateSalvage(Random random,
float valueMult,
float overallMult,
float fuelMult, List<DropData> dropValue, List<DropData> dropRandom) {
928 return generateSalvage(random, valueMult, 1f, overallMult, fuelMult, dropValue, dropRandom);
931 float overallMult,
float fuelMult, List<DropData> dropValue, List<DropData> dropRandom) {
932 if (random ==
null) random =
new Random();
948 if (dropRandom !=
null) {
949 for (DropData data : dropRandom) {
952 int chances = data.chances;
953 if (data.maxChances > chances) {
954 chances = chances + random.nextInt(data.maxChances - chances + 1);
961 float modifiedChances = chances;
962 modifiedChances *= overallMult;
963 if (data.value <= 0) {
964 modifiedChances *= randomMult;
966 modifiedChances *= data.valueMult;
967 float rem = modifiedChances - (int) modifiedChances;
969 chances = (int) modifiedChances + (random.nextFloat() < rem ? 1 : 0);
972 if (picker ==
null && data.group ==
null)
continue;
973 if (picker ==
null) {
980 for (
int i = 0; i < chances; i++) {
994 if (data.value > 0) {
998 qty = (data.value * valueMult * randMult) / baseUnitValue;
1000 if (valueMult <= 0)
continue;
1001 if (qty < 1) qty = 1;
1017 result.
addItems(CargoItemType.SPECIAL,
1027 if (dropValue !=
null) {
1029 for (DropData data : dropValue) {
1032 float maxValue = data.value;
1037 if (data.value > 1) {
1038 maxValue *= valueMult;
1041 maxValue *= overallMult;
1042 maxValue *= data.valueMult;
1045 maxValue *= randMult;
1049 if (picker ==
null && data.group ==
null)
continue;
1050 if (picker ==
null) {
1055 int nothingInARow = 0;
1056 while (value < maxValue && nothingInARow < 10) {
1072 float currValue = baseUnitValue * qty;
1076 if (value <= maxValue) {
1084 if (value <= maxValue) {
1093 result.
addItems(CargoItemType.SPECIAL,
1096 if (value <= maxValue) {
1105 float fuel = result.
getFuel();
1106 if (fuelMult > 1f) {
1107 result.
addFuel((
int) Math.round(fuel * (fuelMult - 1f)));
1149 ShipRecoverySpecialData data =
new ShipRecoverySpecialData(
null);
1151 data.addShip(plugin.
getData().ship.clone());
1152 data.storyPointRecovery =
true;
static SettingsAPI getSettings()
static SoundPlayerAPI getSoundPlayer()
static FactoryAPI getFactory()
static SectorAPI getSector()
static void reportExtraSalvageShown(SectorEntityToken entity)
void modifyPercent(String source, float value)
void modifyMult(String source, float value)
HashMap< String, StatMod > getFlatMods()
void modifyPercentAlways(String source, float value, String desc)
void modifyMultAlways(String source, float value, String desc)
DerelictShipData getData()
static float getAdjustedGantryModifier(CampaignFleetAPI fleet, String skipId, float add)
static final String HEAVY_MACHINERY
static final String DEBRIS_FIELD_SHARED
static final String TAG_MODSPEC
static final String SALVAGE_SPECIAL_DATA
static final String SALVAGE_DEBRIS_FIELD
static final String SALVAGE_SEED
static final String SALVAGE_SPEC_ID_OVERRIDE
static final String SALVAGE_VALUE_MULT_FLEET_NOT_RARE
static final String SALVAGE_VALUE_MULT_FLEET_INCLUDES_RARE
static final String FUEL_SALVAGE_VALUE_MULT_FLEET
static WeightedRandomPicker< DropGroupRow > getPicker(String group)
String getFighterWingId()
String getSpecialItemId()
DropGroupRow resolveToSpecificItem(Random random)
String getSpecialItemData()
List< DropData > getDropValue()
boolean hasTag(String tag)
List< DropData > getDropRandom()
static float getNormalRandom(float min, float max)
static SalvageEntityGenDataSpec getSalvageSpec(String id)
static MemoryAPI getEntityMemory(Map< String, MemoryAPI > memoryMap)
static CargoAPI generateSalvage(Random random, float valueMult, float randomMult, float overallMult, float fuelMult, List< DropData > dropValue, List< DropData > dropRandom)
static CargoAPI generateSalvage(Random random, float valueMult, float overallMult, float fuelMult, List< DropData > dropValue, List< DropData > dropRandom)
void convertToDebrisField(Random random, float valueMult)
void printSalvageModifiers()
float computeOverallMultForDebrisField()
static float FIELD_MIN_SALVAGE_MULT
void showCostDebrisField()
static int FIELD_RADIUS_MAX_REQ_MULT
static int BASE_MACHINERY
boolean canBeMadeRecoverable()
void convertToDebrisField(float valueMult)
static int FIELD_RADIUS_FOR_MAX_REQ
static float getPlayerShipsSalvageModUncapped()
StatModValueGetter getModPrinter()
static float getDebrisReqMult(DebrisFieldTerrainPlugin field)
boolean execute(String ruleId, InteractionDialogAPI dialog, List< Token > params, Map< String, MemoryAPI > memoryMap)
InteractionDialogAPI dialog
MutableStat getValueRecoveryStat(boolean withSkillMultForRares)
CampaignFleetAPI playerFleet
static float SALVAGE_DETECTION_MOD_FLAT
static float FIELD_CONTENT_MULTIPLIER_AFTER_DEMOLITION
static int FIELD_RADIUS_FOR_BASE_REQ
static float FIELD_CONTENT_MULTIPLIER_AFTER_SALVAGE
SalvageEntityGenDataSpec spec
static Map< String, Integer > computeRequiredToSalvage(SectorEntityToken entity)
static float FIELD_SALVAGE_FRACTION_PER_ATTEMPT
static void addExtraSalvage(SectorEntityToken entity, CargoAPI cargo)
static CargoAPI getCombinedExtraSalvage(Map< String, MemoryAPI > memoryMap)
static void clearExtraSalvage(Map< String, MemoryAPI > memoryMap)
SectorEntityToken getEntity()
DebrisFieldParams getParams()
void setScavenged(Boolean scavenged)
static SectorEntityToken addDebrisField(LocationAPI loc, DebrisFieldParams params, Random random)
static String ucFirst(String str)
static Color getNegativeHighlightColor()
static Object getSalvageSpecial(SectorEntityToken entity)
static Random getRandom(long seed, int level)
static void setPrevSalvageSpecial(SectorEntityToken entity, Object data)
static void fadeAndExpire(SectorEntityToken entity)
static long getSalvageSeed(SectorEntityToken entity)
static void setSalvageSpecial(SectorEntityToken entity, Object data)
static Color getHighlightColor()
static void stopPlayerFleet()
static boolean isUnboardable(FleetMemberAPI member)
void setRandom(Random random)
CargoAPI createCargo(boolean unlimitedStacks)
float getFloat(String key)
Vector2f getListenerPos()
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
MutableFleetStatsAPI getStats()
float getQuantity(CargoAPI.CargoItemType type, Object data)
void addWeapons(String id, int count)
float getCommodityQuantity(String id)
void removeCommodity(String id, float quantity)
void addAll(CargoAPI other)
void addFuel(float quantity)
void addItems(CargoAPI.CargoItemType itemType, Object data, float quantity)
void addCommodity(String commodityId, float quantity)
TextPanelAPI getTextPanel()
VisualPanelAPI getVisualPanel()
SectorEntityToken getInteractionTarget()
void setPromptText(String promptText)
OptionPanelAPI getOptionPanel()
void addOption(String text, Object data)
void setShortcut(Object data, int code, boolean ctrl, boolean alt, boolean shift, boolean putLast)
void setNumberOnlyMode(boolean numberOnlyMode)
void setAlignment(Alignment alignment)
void addCost(String commodityId, int quantity, Color color)
void setWithBorder(boolean withBorder)
CampaignFleetAPI getPlayerFleet()
EveryFrameScript addPing(SectorEntityToken entity, String pingType)
FactionAPI getPlayerFaction()
PersonAPI getPlayerPerson()
LocationAPI getContainingLocation()
CustomCampaignEntityPlugin getCustomPlugin()
void addDropRandom(String group, int chances)
String getCustomEntityType()
List< DropData > getDropRandom()
void setFaction(String factionId)
void addDropValue(String group, int value)
void setSensorProfile(Float sensorProfile)
void setDiscoverable(Boolean discoverable)
void setOrbit(OrbitAPI orbit)
boolean hasTag(String tag)
MemoryAPI getMemoryWithoutUpdate()
List< DropData > getDropValue()
LabelAPI addPara(String text)
ResourceCostPanelAPI addCostPanel(String title, float height, Color color, Color dark)
LabelAPI addParagraph(String text)
void appendToLastParagraph(String text)
void highlightInLastPara(Color color, String ...strings)
void highlightLastInLastPara(String text, Color color)
TooltipMakerAPI beginTooltip()
void showLoot(String title, CargoAPI otherCargo, boolean generatePods, CoreInteractionListener listener)
CommoditySpecAPI getCommoditySpec(String commodityId)
String getString(String key)
boolean contains(String key)
void set(String key, Object value)
void addXP(long xp, TextPanelAPI textPanel, boolean withMessage, boolean allowBonusXP, boolean withLevelUp)
MutableCharacterStatsAPI getStats()
ShipHullSpecAPI getHullSpec()
StatBonus getDetectedRangeMod()
DynamicStatsAPI getDynamic()
void addTemporaryModFlat(float durInDays, String source, float value, StatBonus stat)
MutableStat getStat(String id)
float getValue(String id)