1package com.fs.starfarer.api.impl.campaign.econ.impl;
4import java.util.ArrayList;
7import org.json.JSONArray;
8import org.json.JSONException;
9import org.lwjgl.util.vector.Vector2f;
11import com.fs.starfarer.api.EveryFrameScript;
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
14import com.fs.starfarer.api.campaign.SectorEntityToken;
15import com.fs.starfarer.api.campaign.SpecialItemData;
16import com.fs.starfarer.api.campaign.econ.CommodityMarketDataAPI;
17import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
18import com.fs.starfarer.api.campaign.econ.Industry;
19import com.fs.starfarer.api.campaign.econ.MarketAPI;
20import com.fs.starfarer.api.campaign.econ.MarketImmigrationModifier;
21import com.fs.starfarer.api.campaign.listeners.ColonyOtherFactorsListener;
22import com.fs.starfarer.api.characters.MarketConditionSpecAPI;
23import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
24import com.fs.starfarer.api.combat.MutableStat;
25import com.fs.starfarer.api.combat.MutableStat.StatMod;
26import com.fs.starfarer.api.impl.campaign.econ.CommRelayCondition;
27import com.fs.starfarer.api.impl.campaign.econ.impl.ConstructionQueue.ConstructionQueueItem;
28import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
29import com.fs.starfarer.api.impl.campaign.ids.Commodities;
30import com.fs.starfarer.api.impl.campaign.ids.Conditions;
31import com.fs.starfarer.api.impl.campaign.ids.Entities;
32import com.fs.starfarer.api.impl.campaign.ids.Factions;
33import com.fs.starfarer.api.impl.campaign.ids.Industries;
34import com.fs.starfarer.api.impl.campaign.ids.Items;
35import com.fs.starfarer.api.impl.campaign.ids.Stats;
36import com.fs.starfarer.api.impl.campaign.ids.Tags;
37import com.fs.starfarer.api.impl.campaign.population.PopulationComposition;
38import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator;
39import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity;
40import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.EntityLocation;
41import com.fs.starfarer.api.loading.IndustrySpecAPI;
42import com.fs.starfarer.api.ui.TooltipMakerAPI;
43import com.fs.starfarer.api.ui.TooltipMakerAPI.StatModValueGetter;
44import com.fs.starfarer.api.util.Misc;
45import com.fs.starfarer.api.util.Pair;
72 int size =
market.getSize();
74 demand(Commodities.FOOD, size);
76 if (!
market.hasCondition(Conditions.HABITABLE)) {
77 demand(Commodities.ORGANICS, size - 1);
80 int luxuryThreshold = 3;
82 demand(Commodities.DOMESTIC_GOODS, size - 1);
83 demand(Commodities.LUXURY_GOODS, size - luxuryThreshold);
84 demand(Commodities.DRUGS, size - 2);
85 demand(Commodities.ORGANS, size - 3);
87 demand(Commodities.SUPPLIES, Math.min(size, 3));
89 supply(Commodities.CREW, size - 3);
90 supply(Commodities.DRUGS, size - 4);
91 supply(Commodities.ORGANS, size - 5);
94 Pair<String, Integer> deficit =
getMaxDeficit(Commodities.DOMESTIC_GOODS);
95 if (deficit.two <= 0) {
96 market.getStability().modifyFlat(
getModId(0), 1,
"Domestic goods demand met");
102 if (deficit.two <= 0 && size > luxuryThreshold) {
103 market.getStability().modifyFlat(
getModId(1), 1,
"Luxury goods demand met");
109 if (!
market.hasCondition(Conditions.HABITABLE)) {
110 deficit =
getMaxDeficit(Commodities.FOOD, Commodities.ORGANICS);
112 if (deficit.two > 0) {
119 boolean spaceportFirstInQueue =
false;
120 for (ConstructionQueueItem item :
market.getConstructionQueue().getItems()) {
122 if (
spec.hasTag(Industries.TAG_SPACEPORT)) {
123 spaceportFirstInQueue =
true;
127 if (spaceportFirstInQueue && Misc.getCurrentlyBeingConstructed(
market) !=
null) {
128 spaceportFirstInQueue =
false;
130 if (!
market.hasSpaceport() && !spaceportFirstInQueue) {
132 market.getAccessibilityMod().modifyFlat(
getModId(0), accessibilityNoSpaceport,
"No spaceport");
137 market.getAccessibilityMod().modifyFlat(
getModId(1), sizeBonus,
"Colony size");
143 float stability =
market.getPrevStability();
144 float stabilityQualityMod = FleetFactoryV3.getShipQualityModForStability(stability);
145 float doctrineQualityMod =
market.getFaction().getDoctrine().getShipQualityContribution();
147 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).modifyFlatAlways(
getModId(0), stabilityQualityMod,
150 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).modifyFlatAlways(
getModId(1), doctrineQualityMod,
151 Misc.ucFirst(
market.getFaction().getEntityNamePrefix()) +
" fleet doctrine");
154 float stabilityDefenseMult = 0.25f + stability / 10f * 0.75f;
155 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).modifyMultAlways(
getModId(),
156 stabilityDefenseMult,
"Stability");
159 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).modifyFlatAlways(
getModId(),
160 baseDef,
"Base value for a size " +
market.getSize() +
" colony");
165 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).modifyMultAlways(
getModId(1),
166 Math.max(
market.getHazardValue(), 1f),
"Colony hazard rating");
175 FactionDoctrineAPI doctrine =
market.getFaction().getDoctrine();
176 float doctrineShipsMult = FleetFactoryV3.getDoctrineNumShipsMult(doctrine.getNumShips());
177 float marketSizeShipsMult = FleetFactoryV3.getNumShipsMultForMarketSize(
market.getSize());
178 float deficitShipsMult = FleetFactoryV3.getShipDeficitFleetSizeMult(
market);
179 float stabilityShipsMult = FleetFactoryV3.getNumShipsMultForStability(stability);
181 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyFlatAlways(
getModId(0), marketSizeShipsMult,
184 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyMultAlways(
getModId(1), doctrineShipsMult,
185 Misc.ucFirst(
market.getFaction().getEntityNamePrefix()) +
" fleet doctrine");
187 if (deficitShipsMult != 1f) {
188 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyMult(
getModId(2), deficitShipsMult,
191 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyMultAlways(
getModId(2), deficitShipsMult,
192 getDeficitText(Commodities.SHIPS).replaceAll(
"shortage",
"demand met"));
195 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyMultAlways(
getModId(3), stabilityShipsMult,
201 market.getStats().getDynamic().getMod(Stats.OFFICER_PROB_MOD).modifyFlat(
getModId(1),
208 market.getStats().getDynamic().getMod(Stats.ADMIN_PROB_MOD).modifyFlat(
getModId(1),
213 market.addTransientImmigrationModifier(
this);
246 if (marketSize <= 4)
return 0f;
247 if (marketSize == 5)
return 0.1f;
248 if (marketSize == 6)
return 0.15f;
249 if (marketSize == 7)
return 0.2f;
250 if (marketSize == 8)
return 0.25f;
254 if (marketSize <= 1)
return 10;
255 if (marketSize <= 2)
return 20;
256 if (marketSize <= 3)
return 50;
258 return (marketSize - 3) * 100;
272 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).unmodifyFlat(
getModId(0));
273 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).unmodifyFlat(
getModId(1));
275 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).unmodifyFlat(
getModId());
276 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).unmodifyMult(
getModId());
278 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).unmodifyMult(
getModId(1));
281 market.getStats().getDynamic().getMod(Stats.MAX_INDUSTRIES).unmodifyFlat(
getModId());
283 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).unmodifyFlat(
getModId(0));
284 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).unmodifyMult(
getModId(1));
285 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).unmodifyMult(
getModId(2));
286 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).unmodifyMult(
getModId(3));
288 market.getStats().getDynamic().getMod(Stats.OFFICER_PROB_MOD).unmodifyFlat(
getModId(0));
289 market.getStats().getDynamic().getMod(Stats.OFFICER_PROB_MOD).unmodifyFlat(
getModId(1));
290 market.getStats().getDynamic().getMod(Stats.OFFICER_ADDITIONAL_PROB_MULT_MOD).unmodifyFlat(
getModId(0));
291 market.getStats().getDynamic().getMod(Stats.OFFICER_IS_MERC_PROB_MOD).unmodifyFlat(
getModId(0));
292 market.getStats().getDynamic().getMod(Stats.ADMIN_PROB_MOD).unmodifyFlat(
getModId(0));
293 market.getStats().getDynamic().getMod(Stats.ADMIN_PROB_MOD).unmodifyFlat(
getModId(1));
297 market.removeTransientImmigrationModifier(
this);
306 if (mode != IndustryTooltipMode.NORMAL ||
isFunctional()) {
308 MutableStat stabilityMods =
new MutableStat(0);
311 for (StatMod mod :
market.getStability().getFlatMods().values()) {
312 if (mod.source.startsWith(
getModId())) {
313 stabilityMods.modifyFlat(mod.source, mod.value, mod.desc);
318 String totalStr =
"+" + (int)Math.round(total);
319 Color h = Misc.getHighlightColor();
321 totalStr =
"" + (int)Math.round(total);
322 h = Misc.getNegativeHighlightColor();
327 tooltip.addPara(
"Stability bonus: %s", opad, h, totalStr);
329 tooltip.addPara(
"Stability penalty: %s", opad, h, totalStr);
331 tooltip.addStatModGrid(400, 30, opad, pad, stabilityMods,
new StatModValueGetter() {
332 public String getPercentValue(StatMod mod) {
335 public String getMultValue(StatMod mod) {
338 public Color getModColor(StatMod mod) {
339 if (mod.value < 0)
return Misc.getNegativeHighlightColor();
342 public String getFlatValue(StatMod mod) {
393 float size =
market.getSize();
401 return super.getCurrentImage();
406 if (stability <= 5) {
407 return Math.max(0, stability / 5f);
421 float hazardMult = hazard;
423 if (hazardMult < min) hazardMult = min;
430 for (MarketAPI curr :
Global.
getSector().getEconomy().getMarketsCopy()) {
431 if (!curr.isPlayerOwned())
continue;
433 if (curr.getAdmin().isPlayer()) {
438 MutableCharacterStatsAPI stats =
Global.
getSector().getCharacterData().getPerson().getStats();
440 int maxOutposts = stats.getOutpostNumber().getModifiedInt();
442 int overOutposts = outposts - maxOutposts;
446 int penaltyOrBonus = (int) (overOutposts * Misc.getOutpostPenalty());
448 return penaltyOrBonus;
452 if (Misc.getNumIndustries(
market) > Misc.getMaxIndustries(
market)) {
453 market.getStability().modifyFlat(
"_" + modId +
"_overmax", -Misc.OVER_MAX_INDUSTRIES_PENALTY,
"Maximum number of industries exceeded");
455 market.getStability().unmodifyFlat(
"_" + modId +
"_overmax");
474 float inFactionSupply = 0f;
475 float totalDemand = 0f;
476 for (CommodityOnMarketAPI com :
market.getCommoditiesCopy()) {
477 if (com.isNonEcon())
continue;
479 int d = com.getMaxDemand();
480 if (d <= 0)
continue;
483 CommodityMarketDataAPI cmd = com.getCommodityMarketData();
484 int inFaction = Math.max(Math.min(com.getMaxSupply(), com.getAvailable()),
485 Math.min(cmd.getMaxShipping(
market,
true), cmd.getMaxExport(
market.getFactionId())));
486 if (inFaction > d) inFaction = d;
487 if (inFaction < d) inFaction = Math.max(Math.min(com.getMaxSupply(), com.getAvailable()), 0);
493 inFactionSupply += Math.max(0, Math.min(inFaction, com.getAvailable()));
496 if (totalDemand > 0) {
498 float f = inFactionSupply / totalDemand;
502 float mult = Math.round(100f - (f * max * 100f)) / 100f;
503 String desc =
"Demand supplied in-faction (" + (int)Math.round(f * 100f) +
"%)";
504 if (f == 1f) desc =
"All demand supplied in-faction";
505 market.getUpkeepMult().modifyMultAlways(modId +
"ifi", mult, desc);
507 market.getUpkeepMult().modifyMultAlways(modId +
"ifi", 1f,
"All demand supplied out-of-faction; no upkeep reduction");
512 if (
market.isPlayerOwned() &&
market.getAdmin().isPlayer()) {
515 market.getStability().modifyFlat(
"_" + modId +
"_mm", -penalty,
"Mismanagement penalty");
516 }
else if (penalty < 0) {
517 market.getStability().modifyFlat(
"_" + modId +
"_mm", -penalty,
"Management bonus");
519 market.getStability().unmodifyFlat(
"_" + modId +
"_mm");
522 market.getStability().unmodifyFlat(modId +
"_mm");
525 if (!
market.hasCondition(Conditions.COMM_RELAY)) {
532 market.getIncomeMult().unmodifyMult(modId);
533 market.getUpkeepMult().unmodifyMult(modId);
534 market.getUpkeepMult().unmodifyMult(modId +
"ifi");
536 market.getStability().unmodifyFlat(modId);
541 market.getStability().unmodifyFlat(
"_" + modId +
"_mm");
542 market.getStability().unmodifyFlat(
"_" + modId +
"_ms");
543 market.getStability().unmodifyFlat(
"_" + modId +
"_overmax");
545 if (!
market.hasCondition(Conditions.COMM_RELAY)) {
571 int size =
market.getSize();
573 if (size >= 1 && size <= 9) {
574 cid =
"population_" + size;
577 return spec.getDesc() +
"\n\n" + mcs.getDesc().replaceAll(
"\\$marketName",
market.getName());
580 return super.getDescriptionOverride();
588 return "total growth: " + Misc.getRoundedValue(Misc.getMarketSizeProgress(
market) * 100f) +
"%";
591 return super.getBuildOrUpgradeProgressText();
596 if (!super.isBuilding() &&
market.getSize() < Misc.MAX_COLONY_SIZE) {
597 return Misc.getMarketSizeProgress(
market);
599 return super.getBuildOrUpgradeProgress();
606 return super.isBuilding();
611 if (!super.isBuilding() &&
market.getSize() < Misc.MAX_COLONY_SIZE)
return true;
613 return super.isUpgrading();
619 float patherLevel = 0;
620 for (Industry curr :
market.getIndustries()) {
621 patherLevel += getAICoreImpact(curr.getAICoreId());
624 String adminCoreId =
market.getAdmin().getAICoreId();
625 if (adminCoreId !=
null) {
626 patherLevel += 10f * getAICoreImpact(adminCoreId);
629 List<String> targeted =
new ArrayList<String>();
630 targeted.add(Industries.TECHMINING);
631 targeted.add(Industries.HEAVYINDUSTRY);
632 targeted.add(Industries.FUELPROD);
633 targeted.add(Industries.STARFORTRESS);
635 for (String curr : targeted) {
636 if (
market.hasIndustry(curr)) {
641 if (patherLevel > 0) {
642 incoming.add(Factions.LUDDIC_PATH, patherLevel * 0.2f);
651 private float getAICoreImpact(String coreId) {
652 if (Commodities.ALPHA_CORE.equals(coreId))
return 10f;
653 if (Commodities.BETA_CORE.equals(coreId))
return 4f;
654 if (Commodities.GAMMA_CORE.equals(coreId))
return 1f;
672 for (
int i = 0; i <
MAX_IND.length; i++) {
675 }
catch (JSONException e) {
676 throw new RuntimeException(e);
680 if (size < 0) size = 0;
681 if (size > 9) size = 9;
721 market.getStability().unmodifyFlat(
"PAI_improve");
725 public void addImproveDesc(TooltipMakerAPI info, ImprovementDescriptionMode mode) {
727 Color highlight = Misc.getHighlightColor();
730 if (mode == ImprovementDescriptionMode.INDUSTRY_TOOLTIP) {
736 info.addSpacer(opad);
737 super.addImproveDesc(info, mode);
743 protected SectorEntityToken lamp;
744 protected MarketAPI market;
748 this.market = market;
749 this.industry = industry;
751 public void advance(
float amount) {
752 Industry ind = market.getIndustry(Industries.POPULATION);
754 if (item ==
null || !item.getId().equals(Items.ORBITAL_FUSION_LAMP)) {
755 Misc.fadeAndExpire(lamp);
756 industry.lamp =
null;
760 public boolean isDone() {
763 public boolean runWhilePaused() {
770 protected SectorEntityToken
lamp;
784 if (
special !=
null &&
special.getId().equals(Items.ORBITAL_FUSION_LAMP)) {
786 SectorEntityToken focus =
market.getPlanetEntity();
787 if (focus ==
null) focus =
market.getPrimaryEntity();
789 EntityLocation loc =
new EntityLocation();
790 float radius = focus.getRadius() + 100f;
792 radius, radius / (10f + 10f * (
float) Math.random()));
793 AddedEntity added = BaseThemeGenerator.addNonSalvageEntity(
794 market.getContainingLocation(), loc, Entities.FUSION_LAMP,
getMarket().getFactionId());
797 market.getContainingLocation().addScript(
new LampRemover(
lamp,
market,
this));
802 !
market.hasCondition(Conditions.COLD) &&
803 !
market.hasCondition(Conditions.VERY_COLD) &&
804 !
market.hasCondition(Conditions.VERY_HOT)) {
805 if (
market.hasCondition(Conditions.HOT)) {
820 if (
special !=
null)
return false;
822 if (Items.ORBITAL_FUSION_LAMP.equals(data.getId())) {
824 if (
market.hasCondition(mc))
return true;
828 return super.wantsToUseSpecialItem(data);
833 SectorEntityToken nearest =
null;
834 float minDist = Float.MAX_VALUE;
836 for (SectorEntityToken entity :
Global.
getSector().getCustomEntitiesWithTag(Tags.CORONAL_TAP)) {
837 if (!usable || entity.getMemoryWithoutUpdate().contains(
"$usable")) {
838 float dist = Misc.getDistanceLY(locInHyper, entity.getLocationInHyperspace());
840 Math.round(dist * 10f) <= ItemEffectsRepo.CORONAL_TAP_LIGHT_YEARS * 10f) {
843 if (dist < minDist) {
850 if (nearest ==
null)
return null;
852 return new Pair<SectorEntityToken, Float>(nearest, minDist);
855 public static class CoronalTapFactor
implements ColonyOtherFactorsListener {
856 public boolean isActiveFactorFor(SectorEntityToken entity) {
860 public void printOtherFactors(TooltipMakerAPI text, SectorEntityToken entity) {
861 Pair<SectorEntityToken, Float> p =
getNearestCoronalTap(entity.getLocationInHyperspace(),
true);
863 Color h = Misc.getHighlightColor();
866 String dStr =
"" + Misc.getRoundedValueMaxOneAfterDecimal(p.two);
867 String lights =
"light-years";
868 if (dStr.equals(
"1")) lights =
"light-year";
870 if (p.two > ItemEffectsRepo.CORONAL_TAP_LIGHT_YEARS) {
871 text.addPara(
"The nearest coronal tap is located in the " +
872 p.one.getContainingLocation().getNameWithLowercaseType() +
", %s " + lights +
" away. The maximum " +
873 "range at a portal can connect to a tap is %s light-years.",
875 "" + Misc.getRoundedValueMaxOneAfterDecimal(p.two),
876 "" + (
int)ItemEffectsRepo.CORONAL_TAP_LIGHT_YEARS);
878 text.addPara(
"The nearest coronal tap is located in the " +
879 p.one.getContainingLocation().getNameWithLowercaseType() +
", %s " + lights +
" away, allowing " +
880 "a coronal portal located here to connect to it.",
882 "" + Misc.getRoundedValueMaxOneAfterDecimal(p.two));
static SettingsAPI getSettings()
static FactoryAPI getFactory()
static SectorAPI getSector()
static String COMM_RELAY_MOD_ID
static float NO_RELAY_PENALTY
Map< String, MutableCommodityQuantity > supply
String getImprovementsDescForModifiers()
static String getDeficitText(String commodityId)
static int SIZE_FOR_LARGE_IMAGE
SpecialItemData getSpecialItem()
static int SIZE_FOR_SMALL_IMAGE
Map< String, MutableCommodityQuantity > demand
String getNameForModifier()
transient IndustrySpecAPI spec
Pair< String, Integer > getMaxDeficit(String ... commodityIds)
static List< String > FUSION_LAMP_CONDITIONS
static int CORONAL_TAP_LIGHT_YEARS
static float OFFICER_ADDITIONAL_BASE_PROB
static float getUpkeepHazardMult(float hazard)
static float OFFICER_BASE_MERC_PROB
static int getMaxIndustries(int size)
static float IMPROVE_STABILITY_BONUS
static float getIncomeStabilityMult(float stability)
static Pair< SectorEntityToken, Float > getNearestCoronalTap(Vector2f locInHyper, boolean usable)
String getCanNotShutDownReason()
static boolean HAZARD_INCREASES_DEFENSE
boolean wantsToUseSpecialItem(SpecialItemData data)
void addImproveDesc(TooltipMakerAPI info, ImprovementDescriptionMode mode)
static float ADMIN_BASE_PROB
static void unmodifyStability(MarketAPI market, String modId)
static int getMismanagementPenalty()
static float getAccessibilityBonus(int marketSize)
static void modifyStability2(Industry industry, MarketAPI market, String modId)
float getBuildOrUpgradeProgress()
static float OFFICER_PROB_PER_SIZE
static void modifyStability(Industry industry, MarketAPI market, String modId)
static float OFFICER_BASE_PROB
static void modifyUpkeepByHazardRating(MarketAPI market, String modId)
void addPostDemandSection(TooltipMakerAPI tooltip, boolean hasDemand, IndustryTooltipMode mode)
static float ADMIN_PROB_PER_SIZE
boolean hasPostDemandSection(boolean hasDemand, IndustryTooltipMode mode)
String getBuildOrUpgradeProgressText()
void setSpecialItem(SpecialItemData special)
static float getBaseGroundDefenses(int marketSize)
void applyImproveModifiers()
String addedHeatCondition
String removedHeatCondition
String getDescriptionOverride()
void modifyIncoming(MarketAPI market, PopulationComposition incoming)
OrbitAPI createCircularOrbit(SectorEntityToken focus, float angle, float orbitRadius, float orbitDays)
JSONArray getJSONArray(String key)
String getSpriteName(String category, String id)
MarketConditionSpecAPI getMarketConditionSpec(String conditionId)
IndustrySpecAPI getIndustrySpec(String industryId)
float getFloat(String key)