1package com.fs.starfarer.api.impl.campaign.intel.bases;
4import java.util.LinkedHashMap;
6import java.util.Random;
9import org.apache.log4j.Logger;
10import org.json.JSONException;
11import org.json.JSONObject;
13import com.fs.starfarer.api.EveryFrameScript;
14import com.fs.starfarer.api.Global;
15import com.fs.starfarer.api.campaign.BattleAPI;
16import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
17import com.fs.starfarer.api.campaign.CampaignFleetAPI;
18import com.fs.starfarer.api.campaign.FactionAPI;
19import com.fs.starfarer.api.campaign.PersonImportance;
20import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult;
21import com.fs.starfarer.api.campaign.SectorEntityToken;
22import com.fs.starfarer.api.campaign.StarSystemAPI;
23import com.fs.starfarer.api.campaign.TextPanelAPI;
24import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
25import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
26import com.fs.starfarer.api.campaign.econ.EconomyAPI.EconomyUpdateListener;
27import com.fs.starfarer.api.campaign.econ.Industry;
28import com.fs.starfarer.api.campaign.econ.MarketAPI;
29import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel;
30import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
31import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
32import com.fs.starfarer.api.characters.PersonAPI;
33import com.fs.starfarer.api.combat.MutableStat.StatMod;
34import com.fs.starfarer.api.fleet.FleetMemberAPI;
35import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin;
36import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact;
37import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
38import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
39import com.fs.starfarer.api.impl.campaign.DebugFlags;
40import com.fs.starfarer.api.impl.campaign.ids.Conditions;
41import com.fs.starfarer.api.impl.campaign.ids.Entities;
42import com.fs.starfarer.api.impl.campaign.ids.Factions;
43import com.fs.starfarer.api.impl.campaign.ids.Industries;
44import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
45import com.fs.starfarer.api.impl.campaign.ids.Ranks;
46import com.fs.starfarer.api.impl.campaign.ids.Stats;
47import com.fs.starfarer.api.impl.campaign.ids.Submarkets;
48import com.fs.starfarer.api.impl.campaign.ids.Tags;
49import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
50import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResult;
51import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResultType;
52import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarData;
53import com.fs.starfarer.api.impl.campaign.intel.bar.events.LuddicPathBaseBarEvent;
54import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel.BaseBountyData;
55import com.fs.starfarer.api.impl.campaign.intel.deciv.DecivTracker;
56import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel;
57import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidDelegate;
58import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidStageStatus;
59import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames;
60import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames.MarkovNameResult;
61import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator;
62import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity;
63import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.EntityLocation;
64import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.LocationType;
65import com.fs.starfarer.api.ui.Alignment;
66import com.fs.starfarer.api.ui.SectorMapAPI;
67import com.fs.starfarer.api.ui.TooltipMakerAPI;
68import com.fs.starfarer.api.util.IntervalUtil;
69import com.fs.starfarer.api.util.Misc;
70import com.fs.starfarer.api.util.WeightedRandomPicker;
73 EconomyUpdateListener, RaidDelegate {
77 public static String
MEM_FLAG =
"$core_luddicPathBase";
97 protected boolean large =
false;
99 protected Random
random =
new Random();
125 market.getMemoryWithoutUpdate().set(MemFlags.HIDDEN_BASE_MEM_FLAG,
true);
127 market.setFactionId(Factions.LUDDIC_PATH);
129 market.setSurveyLevel(SurveyLevel.FULL);
131 market.setFactionId(factionId);
132 market.addCondition(Conditions.POPULATION_3);
134 market.addIndustry(Industries.POPULATION);
135 market.addIndustry(Industries.SPACEPORT);
136 market.addIndustry(Industries.MILITARYBASE);
138 market.addSubmarket(Submarkets.SUBMARKET_OPEN);
139 market.addSubmarket(Submarkets.SUBMARKET_BLACK);
141 market.getTariff().modifyFlat(
"default_tariff",
market.getFaction().getTariffFraction());
143 LinkedHashMap<LocationType, Float> weights =
new LinkedHashMap<LocationType, Float>();
144 weights.put(LocationType.IN_ASTEROID_BELT, 10f);
145 weights.put(LocationType.IN_ASTEROID_FIELD, 10f);
146 weights.put(LocationType.IN_RING, 10f);
147 weights.put(LocationType.IN_SMALL_NEBULA, 10f);
148 weights.put(LocationType.GAS_GIANT_ORBIT, 10f);
149 weights.put(LocationType.PLANET_ORBIT, 10f);
150 WeightedRandomPicker<EntityLocation> locs = BaseThemeGenerator.getLocations(
null,
system,
null, 100f, weights);
151 EntityLocation loc = locs.pick();
158 AddedEntity added = BaseThemeGenerator.addNonSalvageEntity(
system, loc, Entities.MAKESHIFT_STATION, factionId);
160 if (added ==
null || added.entity ==
null) {
177 BaseThemeGenerator.convertOrbitWithSpin(
entity, -5f);
182 entity.setSensorProfile(1f);
183 entity.setDiscoverable(
true);
184 entity.getDetectedRangeMod().modifyFlat(
"gen", 5000f);
187 market.getMemoryWithoutUpdate().set(DecivTracker.NO_DECIV_KEY,
true);
189 market.reapplyIndustries();
196 baseCommander.setImportanceAndVoice(PersonImportance.HIGH, Misc.random);
214 PortsideBarData.getInstance().addEvent(
new LuddicPathBaseBarEvent(
this));
216 log.info(String.format(
"Added luddic path base in [%s], isLarge: %s",
system.getName(),
"" +
large));
222 if (super.isHidden())
return true;
236 if (status == RaidStageStatus.SUCCESS) {
300 WeightedRandomPicker<String> stations =
new WeightedRandomPicker<String>();
305 JSONObject json =
getFactionForUIColors().getCustom().getJSONObject(Factions.CUSTOM_PATHER_BASES_SMALL);
307 for (String key : JSONObject.getNames(json)) {
308 stations.add(key, (
float) json.optDouble(key, 0f));
310 if (stations.isEmpty()) {
311 stations.add(Industries.ORBITALSTATION, 5f);
313 }
catch (JSONException e) {
317 return stations.pick();
321 for (Industry curr :
market.getIndustries()) {
322 if (curr.getSpec().hasTag(Industries.TAG_STATION)) {
332 String currIndId =
null;
333 if (stationInd !=
null) {
334 currIndId = stationInd.getId();
335 market.removeIndustry(stationInd.getId(),
null,
false);
339 if (currIndId ==
null) {
343 if (currIndId ==
null)
return;
345 market.addIndustry(currIndId);
347 if (stationInd ==
null)
return;
349 stationInd.finishBuildingOrUpgrading();
352 CampaignFleetAPI fleet = Misc.getStationFleet(
entity);
353 if (fleet ==
null)
return;
355 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
356 if (members.size() < 1)
return;
358 fleet.inflateIfNeeded();
382 CampaignFleetAPI fleet = Misc.getStationFleet(
market);
387 fleet.addEventListener(
this);
405 boolean canEndBounty = !
entity.isInCurrentLocation();
406 bountyData.bountyElapsedDays += days;
424 Global.
getSector().getIntelManager().addIntel(
this, text ==
null, text);
437 super.notifyEnding();
438 log.info(String.format(
"Removing luddic path base at [%s]",
system.getName()));
443 Misc.removeRadioChatter(
market);
460 Misc.fadeAndExpire(
entity);
463 result =
new BountyResult(BountyResultType.END_OTHER, 0,
null);
465 if (reason == FleetDespawnReason.DESTROYED_BY_BATTLE &&
466 param instanceof BattleAPI) {
467 BattleAPI battle = (BattleAPI) param;
468 if (battle.isPlayerInvolved()) {
471 payment = (int) (
bountyData.baseBounty * battle.getPlayerInvolvementFraction());
474 Global.
getSector().getPlayerFleet().getCargo().getCredits().add(payment);
476 CustomRepImpact impact =
new CustomRepImpact();
477 impact.delta = bountyData.repChange * battle.getPlayerInvolvementFraction();
478 if (impact.delta < 0.01f) impact.delta = 0.01f;
479 ReputationAdjustmentResult rep =
Global.
getSector().adjustPlayerReputation(
480 new RepActionEnvelope(RepActions.CUSTOM,
481 impact,
null,
null,
false,
true),
484 result =
new BountyResult(BountyResultType.END_PLAYER_BOUNTY, payment, rep);
486 result =
new BountyResult(BountyResultType.END_PLAYER_NO_REWARD, 0,
null);
491 boolean sendUpdate = DebugFlags.SEND_UPDATES_WHEN_NO_COMM ||
492 result.type != BountyResultType.END_OTHER ||
504 ListenerUtil.reportCellDisrupted(cell);
522 Color h = Misc.getHighlightColor();
523 Color g = Misc.getGrayColor();
528 if (mode == ListInfoMode.IN_DESC) initPad = opad;
538 if (isUpdate || mode != ListInfoMode.IN_DESC) {
539 FactionAPI faction =
bountyData.bountyFaction;
540 info.addPara(
"Bounty faction: " + faction.getDisplayName(), initPad, tc,
541 faction.getBaseUIColor(), faction.getDisplayName());
544 info.addPara(
"%s reward", initPad, tc, h, Misc.getDGSCredits(
bountyData.baseBounty));
551 case END_PLAYER_BOUNTY:
552 info.addPara(
"%s received", initPad, tc, h, Misc.getDGSCredits(
result.payment));
554 null,
null, info, tc, isUpdate, 0f);
570 info.addPara(
getName(), c, 0f);
576 return base +
" Base";
583 return base +
" Base - Bounty Posted";
585 return base +
" Base - Bounty Expired";
589 if (
result.type == BountyResultType.END_PLAYER_BOUNTY) {
590 return base +
" Base - Bounty Completed";
591 }
else if (
result.type == BountyResultType.END_PLAYER_NO_REWARD) {
592 return base +
" Base - Destroyed";
596 String name =
market.getName();
599 return base +
" Base - Abandoned";
602 return base +
" Base - Discovered";
604 if (
entity.isDiscoverable()) {
605 return base +
" Base - Exact Location Unknown";
607 return base +
" Base - " + name;
612 return market.getFaction();
621 Color h = Misc.getHighlightColor();
622 Color g = Misc.getGrayColor();
623 Color tc = Misc.getTextColor();
627 FactionAPI faction =
market.getFaction();
629 info.addImage(faction.getLogo(), width, 128, opad);
631 String has = faction.getDisplayNameHasOrHave();
633 info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) +
" " + has +
634 " established a base in the " +
635 market.getContainingLocation().getNameWithLowercaseType() +
". " +
636 "The base serves to provide material support to active Pather cells on nearby colonies, enabling them " +
637 "to cause widespread damage and destruction.",
638 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle());
640 if (!
entity.isDiscoverable()) {
642 info.addPara(
"It has extremely well-developed defensive capabilities " +
643 "and is protected by a large number of fleets.", opad);
645 info.addPara(
"It has well-developed defensive capabilities " +
646 "and is protected by a large number of fleets.", opad);
649 info.addPara(
"You have not yet discovered the exact location or capabilities of this base.", opad);
651 info.addSectionHeading(
"Recent events",
652 faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad);
656 if (!cells.isEmpty()) {
657 float initPad = opad;
659 info.addPara(
"This base is known to be providing support to active Pather cells at the following colonies:", opad);
666 info.addPara(
"You do not know of any active pather cells this base might be providing support to.", opad);
671 info.addPara(Misc.ucFirst(
bountyData.bountyFaction.getDisplayNameWithArticle()) +
" " +
672 bountyData.bountyFaction.getDisplayNameHasOrHave() +
673 " posted a bounty for the destruction of this base.",
674 opad,
bountyData.bountyFaction.getBaseUIColor(),
675 bountyData.bountyFaction.getDisplayNameWithArticleWithoutArticle());
677 if (
result !=
null &&
result.type == BountyResultType.END_PLAYER_BOUNTY) {
678 info.addPara(
"You have successfully completed this bounty.", opad);
685 if (
result.type == BountyResultType.END_PLAYER_NO_REWARD) {
686 info.addPara(
"You have destroyed this base.", opad);
687 }
else if (
result.type == BountyResultType.END_OTHER) {
688 info.addPara(
"It is rumored that this base is no longer operational.", opad);
700 Set<String> tags = super.getIntelTags(map);
702 tags.add(Tags.INTEL_BOUNTY);
704 tags.add(Tags.INTEL_EXPLORATION);
711 if (cell.getMarket().isPlayerOwned() && !cell.isSleeper()) {
712 tags.add(Tags.INTEL_COLONIES);
717 tags.add(
market.getFactionId());
726 if (
market.getPrimaryEntity().isDiscoverable()) {
727 return system.getCenter();
729 return market.getPrimaryEntity();
735 MarkovNames.loadIfNeeded();
737 MarkovNameResult gen =
null;
738 for (
int i = 0; i < 10; i++) {
739 gen = MarkovNames.generate(
null);
741 String test = gen.name;
742 if (test.toLowerCase().startsWith(
"the "))
continue;
743 String p = pickPostfix();
744 if (p !=
null && !p.isEmpty()) {
747 if (test.length() > 22)
continue;
755 private String pickPostfix() {
756 WeightedRandomPicker<String> post =
new WeightedRandomPicker<String>();
767 post.add(
"Sanctuary");
771 post.add(
"Safehold");
772 post.add(
"Terminus");
773 post.add(
"Principle");
774 post.add(
"Offering");
775 post.add(
"Devotion");
776 post.add(
"Atonement");
777 post.add(
"Cleansing");
778 post.add(
"Oblation");
779 post.add(
"Sacrement");
784 CommodityOnMarketAPI com =
market.getCommodityData(commodityId);
786 String modId =
market.getId();
787 StatMod mod = com.getAvailableStat().getFlatStatMod(modId);
789 curr = Math.round(mod.value);
792 int a = com.getAvailable() - curr;
793 int d = com.getMaxDemand();
795 com.getAvailableStat().modifyFlat(modId, (d - a),
"Brought in by smugglers");
800 float qualityBonus = 0f;
817 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).
818 modifyFlatAlways(
market.getId(), qualityBonus,
819 "Development level");
821 float fleetSizeBonus = 0.5f;
822 if (
large) fleetSizeBonus = 1f;
823 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyFlatAlways(
market.getId(),
825 "Development level");
827 String modId =
market.getId();
828 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).modifyFlat(modId, light);
829 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).modifyFlat(modId, medium);
830 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).modifyFlat(modId, heavy);
845 for (IntelInfoPlugin curr : bases) {
847 if (intel !=
this && intel.
bountyData !=
null) {
853 float base = 100000f;
856 bountyData.repChange = 0.05f;
859 bountyData.repChange = 0.1f;
862 bountyData.baseBounty = base * (0.9f + (float) Math.random() * 0.2f);
863 bountyData.baseBounty = (int)(
bountyData.baseBounty / 10000) * 10000;
866 WeightedRandomPicker<FactionAPI> picker =
new WeightedRandomPicker<FactionAPI>();
868 FactionAPI faction = cell.getMarket().getFaction();
870 picker.add(faction, (
float) Math.pow(2f, cell.getMarket().getSize()));
873 FactionAPI faction = picker.pick();
875 if (faction ==
null || faction.isPlayerFaction()) {
880 bountyData.bountyFaction = faction;
881 bountyData.bountyDuration = 180f;
882 bountyData.bountyElapsedDays = 0f;
885 Misc.makeImportant(
entity,
"baseBounty");
898 Misc.makeUnimportant(
entity,
"baseBounty");
static SettingsAPI getSettings()
static FactoryAPI getFactory()
static Logger getLogger(Class c)
static SectorAPI getSector()
static void addAdjustmentMessage(float delta, FactionAPI faction, PersonAPI person, TextPanelAPI panel, TooltipMakerAPI info, Color tc, boolean withCurrent, float pad)
static boolean SEND_UPDATES_WHEN_NO_COMM
static boolean PATHER_BASE_DEBUG
void sendUpdate(Object listInfoParam, TextPanelAPI textPanel)
void unindent(TooltipMakerAPI info)
void addDays(TooltipMakerAPI info, String after, float days)
Long getPlayerVisibleTimestamp()
void sendUpdateIfPlayerHasIntel(Object listInfoParam, TextPanelAPI textPanel)
Object getListInfoParam()
Color getBulletColorForMode(ListInfoMode mode)
void bullet(TooltipMakerAPI info)
Color getTitleColor(ListInfoMode mode)
static void addMarketToList(TooltipMakerAPI info, MarketAPI market, float pad)
void notifyRaidEnded(RaidIntel raid, RaidStageStatus status)
static LuddicPathBaseIntel getIntelFor(MarketAPI market)
void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode)
void setBaseCommander(PersonAPI baseCommander)
Set< String > getIntelTags(SectorMapAPI map)
String pickStationType(boolean large)
SectorEntityToken getEntity()
void updateStationIfNeeded(boolean large)
static final String PATHER_BASE_COMMANDER
BaseBountyData bountyData
CampaignFleetAPI addedListenerTo
void createSmallDescription(TooltipMakerAPI info, float width, float height)
LuddicPathBaseIntel(StarSystemAPI system, String factionId)
IntervalUtil monthlyInterval
float getTimeRemainingFraction()
boolean isEconomyListenerExpired()
void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode)
Industry getStationIndustry()
void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param)
static LuddicPathBaseIntel getIntelFor(StarSystemAPI system)
List< ArrowData > getArrowData(SectorMapAPI map)
static Object BOUNTY_EXPIRED_PARAM
String getSmallDescriptionTitle()
void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle)
static Object DISCOVERED_PARAM
PersonAPI getBaseCommander()
void makeKnown(TextPanelAPI text)
SectorEntityToken getMapLocation(SectorMapAPI map)
void commodityUpdated(String commodityId)
void advanceImpl(float amount)
FactionAPI getFactionForUIColors()
StarSystemAPI getSystem()
static LuddicPathBaseManager getInstance()
static List< LuddicPathCellsIntel > getCellsForBase(LuddicPathBaseIntel base, boolean includeSleeper)
static Object UPDATE_DISRUPTED
static void markRecentlyUsedForBase(StarSystemAPI system)
MarketAPI createMarket(String id, String name, int size)
String getSpriteName(String category, String id)
float getFloat(String key)