1package com.fs.starfarer.api.impl.campaign.econ.impl;
4import java.util.Random;
6import org.lwjgl.util.vector.Vector2f;
8import com.fs.starfarer.api.Global;
9import com.fs.starfarer.api.campaign.BattleAPI;
10import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
11import com.fs.starfarer.api.campaign.CampaignFleetAPI;
12import com.fs.starfarer.api.campaign.PlanetAPI;
13import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
14import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
15import com.fs.starfarer.api.campaign.econ.Industry;
16import com.fs.starfarer.api.campaign.econ.MarketAPI;
17import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
18import com.fs.starfarer.api.campaign.rules.MemoryAPI;
19import com.fs.starfarer.api.impl.campaign.DebugFlags;
20import com.fs.starfarer.api.impl.campaign.fleets.FleetFactory.PatrolType;
21import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
22import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
23import com.fs.starfarer.api.impl.campaign.fleets.PatrolAssignmentAIV4;
24import com.fs.starfarer.api.impl.campaign.fleets.RouteManager;
25import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
26import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
27import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
28import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
29import com.fs.starfarer.api.impl.campaign.ids.Commodities;
30import com.fs.starfarer.api.impl.campaign.ids.Factions;
31import com.fs.starfarer.api.impl.campaign.ids.Industries;
32import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
33import com.fs.starfarer.api.impl.campaign.ids.Ranks;
34import com.fs.starfarer.api.impl.campaign.ids.Stats;
35import com.fs.starfarer.api.impl.campaign.ids.Strings;
36import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD.RaidDangerLevel;
37import com.fs.starfarer.api.ui.TooltipMakerAPI;
38import com.fs.starfarer.api.util.IntervalUtil;
39import com.fs.starfarer.api.util.Misc;
40import com.fs.starfarer.api.util.Pair;
41import com.fs.starfarer.api.util.WeightedRandomPicker;
60 int size =
market.getSize();
62 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
63 boolean militaryBase =
getSpec().hasTag(Industries.TAG_MILITARY);
64 boolean command =
getSpec().hasTag(Industries.TAG_COMMAND);
79 }
else if (militaryBase) {
94 }
else if (size == 4) {
98 }
else if (size == 5) {
102 }
else if (size == 6) {
106 }
else if (size == 7) {
110 }
else if (size == 8) {
114 }
else if (size >= 9) {
121 if (militaryBase || command) {
123 medium = Math.max(medium + 1, size / 2 - 1);
124 heavy = Math.max(heavy, medium - 1);
146 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).modifyFlat(
getModId(), light);
147 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).modifyFlat(
getModId(), medium);
148 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).modifyFlat(
getModId(), heavy);
151 demand(Commodities.SUPPLIES, size - 1 + extraDemand);
152 demand(Commodities.FUEL, size - 1 + extraDemand);
153 demand(Commodities.SHIPS, size - 1 + extraDemand);
155 supply(Commodities.CREW, size);
159 supply(Commodities.MARINES, size);
177 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD)
181 MemoryAPI memory =
market.getMemoryWithoutUpdate();
182 Misc.setFlagWithReason(memory, MemFlags.MARKET_PATROL,
getModId(),
true, -1);
184 if (militaryBase || command) {
185 Misc.setFlagWithReason(memory, MemFlags.MARKET_MILITARY,
getModId(),
true, -1);
191 market.getStats().getDynamic().getMod(Stats.OFFICER_PROB_MOD).modifyFlat(
getModId(0), officerProb);
205 MemoryAPI memory =
market.getMemoryWithoutUpdate();
206 Misc.setFlagWithReason(memory, MemFlags.MARKET_PATROL,
getModId(),
false, -1);
207 Misc.setFlagWithReason(memory, MemFlags.MARKET_MILITARY,
getModId(),
false, -1);
214 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).unmodifyFlat(
getModId());
215 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).unmodifyFlat(
getModId());
216 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).unmodifyFlat(
getModId());
218 market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD).unmodifyMult(
getModId());
220 market.getStats().getDynamic().getMod(Stats.OFFICER_PROB_MOD).unmodifyFlat(
getModId(0));
224 return mode != IndustryTooltipMode.NORMAL ||
isFunctional();
229 if (mode != IndustryTooltipMode.NORMAL ||
isFunctional()) {
232 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
233 boolean command =
getSpec().hasTag(Industries.TAG_COMMAND);
243 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
244 boolean militaryBase =
getSpec().hasTag(Industries.TAG_MILITARY);
245 boolean command =
getSpec().hasTag(Industries.TAG_COMMAND);
246 int stabilityMod = 1;
249 }
else if (militaryBase) {
251 }
else if (command) {
260 if (
getSpec().getName().contains(
"HQ")) {
264 return Misc.ucFirst(
getSpec().getName().toLowerCase());
274 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
276 return getMaxDeficit(Commodities.SUPPLIES, Commodities.FUEL, Commodities.SHIPS);
279 return getMaxDeficit(Commodities.SUPPLIES, Commodities.FUEL, Commodities.SHIPS);
285 boolean military = Industries.MILITARYBASE.equals(
getId());
287 PlanetAPI planet =
market.getPlanetEntity();
288 if (planet ==
null || planet.isGasGiant()) {
292 return super.getCurrentImage();
305 boolean canBuild =
false;
306 for (Industry ind :
market.getIndustries()) {
307 if (ind ==
this)
continue;
308 if (!ind.isFunctional())
continue;
309 if (ind.getSpec().hasTag(Industries.TAG_SPACEPORT)) {
318 return "Requires a functional spaceport";
330 super.buildingFinished();
332 tracker.forceIntervalElapsed();
337 super.upgradeFinished(previous);
339 tracker.forceIntervalElapsed();
344 super.advance(amount);
356 float spawnRate = 1f;
357 float rateMult =
market.getStats().getDynamic().getStat(Stats.COMBAT_FLEET_SPAWN_RATE_MULT).getModifiedValue();
358 spawnRate *= rateMult;
364 float extraTime = 0f;
367 float interval =
tracker.getIntervalDuration();
368 extraTime = interval * days;
372 tracker.advance(days * spawnRate + extraTime);
376 tracker.advance(days * spawnRate * 100f);
379 if (
tracker.intervalElapsed()) {
388 int light =
getCount(PatrolType.FAST);
389 int medium =
getCount(PatrolType.COMBAT);
390 int heavy =
getCount(PatrolType.HEAVY);
396 WeightedRandomPicker<PatrolType> picker =
new WeightedRandomPicker<PatrolType>();
397 picker.add(PatrolType.HEAVY, maxHeavy - heavy);
398 picker.add(PatrolType.COMBAT, maxMedium - medium);
399 picker.add(PatrolType.FAST, maxLight - light);
401 if (picker.isEmpty())
return;
403 PatrolType type = picker.pick();
404 PatrolFleetData custom =
new PatrolFleetData(type);
406 OptionalFleetData extra =
new OptionalFleetData(
market);
407 extra.fleetType = type.getFleetType();
409 RouteData route = RouteManager.getInstance().addRoute(sid,
market, Misc.genRandomSeed(), extra,
this, custom);
411 extra.strength = Misc.getAdjustedStrength(extra.strength,
market);
414 float patrolDays = 35f + (float) Math.random() * 10f;
415 route.addSegment(
new RouteSegment(patrolDays,
market.getPrimaryEntity()));
419 public static class PatrolFleetData {
420 public PatrolType type;
423 public PatrolFleetData(PatrolType type) {
444 for (RouteData data : RouteManager.getInstance().getRoutesForSource(
getRouteSourceId())) {
445 if (data.getCustom() instanceof PatrolFleetData) {
446 PatrolFleetData custom = (PatrolFleetData) data.getCustom();
447 for (PatrolType type : types) {
448 if (type == custom.type) {
459 if (type == PatrolType.FAST) {
460 return (
int)
market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).computeEffective(0);
462 if (type == PatrolType.COMBAT) {
463 return (
int)
market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).computeEffective(0);
465 if (type == PatrolType.HEAVY) {
466 return (
int)
market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).computeEffective(0);
472 return getMarket().getId() +
"_" +
"military";
487 if (reason == FleetDespawnReason.REACHED_DESTINATION) {
488 RouteData route = RouteManager.getInstance().getRoute(
getRouteSourceId(), fleet);
489 if (route.getCustom() instanceof PatrolFleetData) {
490 PatrolFleetData custom = (PatrolFleetData) route.getCustom();
491 if (custom.spawnFP > 0) {
492 float fraction = fleet.getFleetPoints() / custom.spawnFP;
503 combat = Math.round(3f + (
float) random.nextFloat() * 2f) * 5f;
506 combat = Math.round(6f + (
float) random.nextFloat() * 3f) * 5f;
509 combat = Math.round(10f + (
float) random.nextFloat() * 5f) * 5f;
512 return (
int) Math.round(combat);
517 PatrolFleetData custom = (PatrolFleetData) route.getCustom();
518 PatrolType type = custom.type;
520 Random random = route.getRandom();
524 if (fleet ==
null || fleet.isEmpty())
return null;
526 fleet.addEventListener(
this);
528 market.getContainingLocation().addEntity(fleet);
529 fleet.setFacing((
float) Math.random() * 360f);
531 fleet.setLocation(
market.getPrimaryEntity().getLocation().x,
market.getPrimaryEntity().getLocation().y);
533 fleet.addScript(
new PatrolAssignmentAIV4(fleet, route));
535 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_IGNORES_OTHER_FLEETS,
true, 0.3f);
540 if (custom.spawnFP <= 0) {
541 custom.spawnFP = fleet.getFleetPoints();
547 public static CampaignFleetAPI
createPatrol(PatrolType type, String factionId, RouteData route, MarketAPI
market, Vector2f locInHyper, Random random) {
548 if (random ==
null) random =
new Random();
553 float freighter = 0f;
554 String fleetType = type.getFleetType();
559 tanker = Math.round((
float) random.nextFloat() * 5f);
562 tanker = Math.round((
float) random.nextFloat() * 10f);
563 freighter = Math.round((
float) random.nextFloat() * 10f);
567 FleetParamsV3 params =
new FleetParamsV3(
571 route ==
null ?
null : route.getQualityOverride(),
582 params.timestamp = route.getTimestamp();
584 params.random = random;
585 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
587 if (fleet ==
null || fleet.isEmpty())
return null;
589 if (!fleet.getFaction().getCustomBoolean(Factions.CUSTOM_PATROLS_HAVE_NO_PATROL_MEMORY_KEY)) {
590 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PATROL_FLEET,
true);
591 if (type == PatrolType.FAST || type == PatrolType.COMBAT) {
592 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_CUSTOMS_INSPECTOR,
true);
594 }
else if (fleet.getFaction().getCustomBoolean(Factions.CUSTOM_PIRATE_BEHAVIOR)) {
595 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE,
true);
600 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_RAIDER,
true);
604 String postId = Ranks.POST_PATROL_COMMANDER;
605 String rankId = Ranks.SPACE_COMMANDER;
608 rankId = Ranks.SPACE_LIEUTENANT;
611 rankId = Ranks.SPACE_COMMANDER;
614 rankId = Ranks.SPACE_CAPTAIN;
618 fleet.getCommander().setPostId(postId);
619 fleet.getCommander().setRankId(rankId);
629 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyMult(
635 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).unmodifyMult(
getModId());
645 Color highlight = Misc.getHighlightColor();
647 String pre =
"Alpha-level AI core currently assigned. ";
648 if (mode == AICoreDescriptionMode.MANAGE_CORE_DIALOG_LIST || mode == AICoreDescriptionMode.INDUSTRY_TOOLTIP) {
649 pre =
"Alpha-level AI core. ";
653 String str = Strings.X + (1f + a);
655 if (mode == AICoreDescriptionMode.INDUSTRY_TOOLTIP) {
657 TooltipMakerAPI text = tooltip.beginImageWithText(coreSpec.getIconName(), 48);
658 text.addPara(pre +
"Reduces upkeep cost by %s. Reduces demand by %s unit. " +
659 "Increases fleet size by %s.", 0f, highlight,
662 tooltip.addImageWithText(opad);
666 tooltip.addPara(pre +
"Reduces upkeep cost by %s. Reduces demand by %s unit. " +
667 "Increases fleet size by %s.", opad, highlight,
681 String key =
"mil_base_improve";
683 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
693 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).unmodifyFlat(key);
694 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).unmodifyFlat(key);
698 public void addImproveDesc(TooltipMakerAPI info, ImprovementDescriptionMode mode) {
700 Color highlight = Misc.getHighlightColor();
704 boolean patrol =
getSpec().hasTag(Industries.TAG_PATROL);
705 String type =
"medium patrols";
706 if (!patrol) type =
"heavy patrols";
708 if (mode == ImprovementDescriptionMode.INDUSTRY_TOOLTIP) {
709 info.addPara(
"Number of " + type +
" launched increased by %s.", 0f, highlight, str);
711 info.addPara(
"Increases the number of " + type +
" launched by %s.", 0f, highlight, str);
714 info.addSpacer(opad);
715 super.addImproveDesc(info, mode);
static SettingsAPI getSettings()
static SectorAPI getSector()
static boolean FAST_PATROL_SPAWN
IndustrySpecAPI getSpec()
static int DEMAND_REDUCTION
void applyIncomeAndUpkeep(float sizeOverride)
Map< String, MutableCommodityQuantity > supply
static String getDeficitText(String commodityId)
void addStabilityPostDemandSection(TooltipMakerAPI tooltip, boolean hasDemand, IndustryTooltipMode mode)
void unmodifyStabilityWithBaseMod()
void modifyStabilityWithBaseMod()
float getDeficitMult(String ... commodities)
void addGroundDefensesImpactSection(TooltipMakerAPI tooltip, float bonus, String ...commodities)
Map< String, MutableCommodityQuantity > demand
MutableStat demandReduction
Pair< String, Integer > getMaxDeficit(String ... commodityIds)
void applyAlphaCoreModifiers()
static float DEFENSE_BONUS_MILITARY
static float DEFENSE_BONUS_PATROL
void applyImproveModifiers()
boolean isAvailableToBuild()
void addImproveDesc(TooltipMakerAPI info, ImprovementDescriptionMode mode)
boolean shouldCancelRouteAfterDelayCheck(RouteData route)
static CampaignFleetAPI createPatrol(PatrolType type, String factionId, RouteData route, MarketAPI market, Vector2f locInHyper, Random random)
Pair< String, Integer > getStabilityAffectingDeficit()
int getCount(PatrolType ... types)
void applyAlphaCoreSupplyAndDemandModifiers()
void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle)
String getRouteSourceId()
void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param)
static float DEFENSE_BONUS_COMMAND
String getNameForModifier()
String getUnavailableReason()
boolean shouldRepeat(RouteData route)
boolean isDemandLegal(CommodityOnMarketAPI com)
RaidDangerLevel adjustCommodityDangerLevel(String commodityId, RaidDangerLevel level)
void upgradeFinished(Industry previous)
float returningPatrolValue
boolean isSupplyLegal(CommodityOnMarketAPI com)
RaidDangerLevel adjustItemDangerLevel(String itemId, String data, RaidDangerLevel level)
int getBaseStabilityMod()
static float OFFICER_PROB_MOD_PATROL_HQ
boolean hasPostDemandSection(boolean hasDemand, IndustryTooltipMode mode)
void reportAboutToBeDespawnedByRouteManager(RouteData route)
static float ALPHA_CORE_BONUS
void addPostDemandSection(TooltipMakerAPI tooltip, boolean hasDemand, IndustryTooltipMode mode)
int getMaxPatrols(PatrolType type)
void addAlphaCoreDescription(TooltipMakerAPI tooltip, AICoreDescriptionMode mode)
static int getPatrolCombatFP(PatrolType type, Random random)
void advance(float amount)
static int IMPROVE_NUM_PATROLS_BONUS
void applyNoAICoreModifiers()
static float OFFICER_PROB_MOD_MILITARY_BASE
CampaignFleetAPI spawnFleet(RouteData route)
static float OFFICER_PROB_MOD_HIGH_COMMAND
String getSpriteName(String category, String id)
CommoditySpecAPI getCommoditySpec(String commodityId)
float getFloat(String key)