1package com.fs.starfarer.api.impl.campaign.procgen.themes;
4import java.util.ArrayList;
5import java.util.Collections;
6import java.util.Comparator;
9import org.lwjgl.util.vector.Vector2f;
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.campaign.AICoreOfficerPlugin;
13import com.fs.starfarer.api.campaign.CampaignFleetAPI;
14import com.fs.starfarer.api.campaign.CargoAPI;
15import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
16import com.fs.starfarer.api.campaign.InteractionDialogAPI;
17import com.fs.starfarer.api.campaign.JumpPointAPI;
18import com.fs.starfarer.api.campaign.JumpPointAPI.JumpDestination;
19import com.fs.starfarer.api.campaign.OrbitAPI;
20import com.fs.starfarer.api.campaign.PlanetAPI;
21import com.fs.starfarer.api.campaign.SectorEntityToken;
22import com.fs.starfarer.api.campaign.StarSystemAPI;
23import com.fs.starfarer.api.characters.PersonAPI;
24import com.fs.starfarer.api.combat.BattleCreationContext;
25import com.fs.starfarer.api.fleet.FleetMemberAPI;
26import com.fs.starfarer.api.fleet.FleetMemberType;
27import com.fs.starfarer.api.impl.campaign.FleetEncounterContext;
28import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.BaseFIDDelegate;
29import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.FIDConfig;
30import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.FIDConfigGen;
31import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
32import com.fs.starfarer.api.impl.campaign.ids.Abilities;
33import com.fs.starfarer.api.impl.campaign.ids.Commodities;
34import com.fs.starfarer.api.impl.campaign.ids.Entities;
35import com.fs.starfarer.api.impl.campaign.ids.Factions;
36import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
37import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
38import com.fs.starfarer.api.impl.campaign.ids.Tags;
39import com.fs.starfarer.api.impl.campaign.procgen.Constellation;
40import com.fs.starfarer.api.impl.campaign.procgen.DefenderDataOverride;
41import com.fs.starfarer.api.impl.campaign.procgen.NameAssigner;
42import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
43import com.fs.starfarer.api.impl.campaign.procgen.themes.RemnantSeededFleetManager.RemnantFleetInteractionConfigGen;
44import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner.SpecialCreationContext;
45import com.fs.starfarer.api.util.Misc;
46import com.fs.starfarer.api.util.WeightedRandomPicker;
51 public static enum RemnantSystemType {
52 DESTROYED(Tags.THEME_REMNANT_DESTROYED,
"$remnantDestroyed"),
53 SUPPRESSED(Tags.THEME_REMNANT_SUPPRESSED,
"$remnantSuppressed"),
54 RESURGENT(Tags.THEME_REMNANT_RESURGENT,
"$remnantResurgent"),
58 private String beaconFlag;
59 private RemnantSystemType(String tag, String beaconFlag) {
61 this.beaconFlag = beaconFlag;
63 public String getTag() {
66 public String getBeaconFlag() {
86 if (total <= 0)
return;
90 if (num > total) num = (int) total;
93 int numDestroyed = (int) (num * (0.23f + 0.1f *
random.nextFloat()));
94 if (numDestroyed < 1) numDestroyed = 1;
95 int numSuppressed = (int) (num * (0.23f + 0.1f *
random.nextFloat()));
96 if (numSuppressed < 1) numSuppressed = 1;
98 float suppressedStationMult = 0.5f;
99 int suppressedStations = (int) Math.ceil(numSuppressed * suppressedStationMult);
101 WeightedRandomPicker<Boolean> addSuppressedStation =
new WeightedRandomPicker<Boolean>(
random);
102 for (
int i = 0; i < numSuppressed; i++) {
103 if (i < suppressedStations) {
104 addSuppressedStation.add(
true, 1f);
106 addSuppressedStation.add(
false, 1f);
111 Collections.reverse(constellations);
114 if (total < num / (1f - skipProb)) {
115 skipProb = 1f - (num / total);
119 List<StarSystemData> remnantSystems =
new ArrayList<StarSystemData>();
121 if (
DEBUG) System.out.println(
"\n\n\n");
122 if (
DEBUG) System.out.println(
"Generating remnant systems");
127 for (
int i = 0; i < num && i < constellations.size(); i++) {
129 if (
random.nextFloat() < skipProb) {
130 if (
DEBUG) System.out.println(
"Skipping constellation " + c.
getName());
135 List<StarSystemData> systems =
new ArrayList<StarSystemData>();
143 int numMain = 1 +
random.nextInt(2);
144 if (numMain > mainCandidates.size()) numMain = mainCandidates.size();
146 if (
DEBUG) System.out.println(
"Skipping constellation " + c.
getName() +
", no suitable main candidates");
150 RemnantSystemType type = RemnantSystemType.RESURGENT;
151 if (numUsed < numDestroyed) {
152 type = RemnantSystemType.DESTROYED;
153 }
else if (numUsed < numDestroyed + numSuppressed) {
154 type = RemnantSystemType.SUPPRESSED;
163 if (
DEBUG) System.out.println(
"Generating " + numMain +
" main systems in " + c.
getName());
164 for (
int j = 0; j < numMain; j++) {
165 StarSystemData data = mainCandidates.get(j);
168 data.system.addTag(Tags.THEME_INTERESTING);
169 data.system.addTag(Tags.THEME_REMNANT);
170 if (type != RemnantSystemType.DESTROYED) {
171 data.system.addTag(Tags.THEME_UNSAFE);
173 data.system.addTag(Tags.THEME_REMNANT_MAIN);
174 data.system.addTag(type.getTag());
175 remnantSystems.add(data);
182 if (type == RemnantSystemType.DESTROYED) {
184 data.system.addScript(fleets);
185 }
else if (type == RemnantSystemType.SUPPRESSED) {
187 data.system.addScript(fleets);
190 if (j == 0 && !addSuppressedStation.isEmpty()) addSuppressedStation.pickAndRemove();
193 for (CampaignFleetAPI station : stations) {
194 int maxFleets = 2 +
random.nextInt(3);
196 station, 1f, 0, maxFleets, 25f, 6, 12);
197 data.system.addScript(activeFleets);
201 }
else if (type == RemnantSystemType.RESURGENT) {
203 for (CampaignFleetAPI station : stations) {
204 int maxFleets = 8 +
random.nextInt(5);
206 station, 1f, 0, maxFleets, 15f, 8, 24);
207 data.system.addScript(activeFleets);
212 for (StarSystemData data : systems) {
213 int index = mainCandidates.indexOf(data);
214 if (index >= 0 && index < numMain)
continue;
218 if (type == RemnantSystemType.DESTROYED) {
219 data.system.addTag(Tags.THEME_INTERESTING_MINOR);
221 data.system.addTag(Tags.THEME_INTERESTING);
223 data.system.addTag(Tags.THEME_REMNANT);
225 data.system.addTag(Tags.THEME_REMNANT_SECONDARY);
226 data.system.addTag(type.getTag());
227 remnantSystems.add(data);
229 if (
random.nextFloat() < 0.5f) {
231 data.system.addScript(fleets);
233 data.system.addTag(Tags.THEME_REMNANT_NO_FLEETS);
243 SpecialCreationContext specialContext =
new SpecialCreationContext();
249 if (
DEBUG) System.out.println(
"Finished generating remnant systems\n\n\n\n\n");
256 for (StarSystemData data : systemData) {
264 if (data.system.hasTag(Tags.THEME_REMNANT_SECONDARY)) {
268 for (AddedEntity added : data.generated) {
269 if (added.entityType ==
null)
continue;
270 if (Entities.WRECK.equals(added.entityType))
continue;
275 if (Entities.STATION_MINING_REMNANT.equals(added.entityType)) {
279 }
else if (Entities.ORBITAL_HABITAT_REMNANT.equals(added.entityType)) {
283 }
else if (Entities.STATION_RESEARCH_REMNANT.equals(added.entityType)) {
295 if (min < 1) min = 1;
296 if (max < 1) max = 1;
298 if (
random.nextFloat() < prob) {
299 Misc.setDefenderOverride(added.entity,
new DefenderDataOverride(Factions.REMNANTS, 1f, min, max, 4));
308 if (
DEBUG) System.out.println(
" Generating secondary remnant system in " + data.system.getName());
309 boolean special = data.isBlackHole() || data.isNebula() || data.isPulsar();
314 if (
random.nextFloat() < 0.5f)
return;
316 if (!data.resourceRich.isEmpty()) {
320 if (!special && !data.habitable.isEmpty()) {
327 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f));
333 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f));
336 Entities.WEAPONS_CACHE_REMNANT, 4f,
337 Entities.WEAPONS_CACHE_SMALL_REMNANT, 10f,
338 Entities.SUPPLY_CACHE, 4f,
339 Entities.SUPPLY_CACHE_SMALL, 10f,
340 Entities.EQUIPMENT_CACHE, 4f,
341 Entities.EQUIPMENT_CACHE_SMALL, 10f
347 public void populateMain(StarSystemData data, RemnantSystemType type) {
349 if (
DEBUG) System.out.println(
" Generating remnant center in " + data.system.getName());
351 StarSystemAPI system = data.system;
355 if (
DEBUG) System.out.println(
" Added warning beacon");
357 int maxHabCenters = 1 +
random.nextInt(3);
359 HabitationLevel level = HabitationLevel.LOW;
360 if (maxHabCenters == 2) level = HabitationLevel.MEDIUM;
361 if (maxHabCenters >= 3) level = HabitationLevel.HIGH;
367 float probRelay = 1f;
368 float probMining = 0.5f;
369 float probResearch = 0.25f;
389 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f));
392 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f));
410 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f));
413 Entities.WEAPONS_CACHE_REMNANT, 10f,
414 Entities.WEAPONS_CACHE_SMALL_REMNANT, 10f,
415 Entities.SUPPLY_CACHE, 10f,
416 Entities.SUPPLY_CACHE_SMALL, 10f,
417 Entities.EQUIPMENT_CACHE, 10f,
418 Entities.EQUIPMENT_CACHE_SMALL, 10f
426 List<StarSystemData> result =
new ArrayList<StarSystemData>();
428 for (StarSystemData data : systems) {
429 if (data.isBlackHole() || data.isNebula() || data.isPulsar())
continue;
431 if (data.planets.size() >= 3 || data.habitable.size() >= 1) {
442 Collections.sort(systems,
new Comparator<StarSystemData>() {
443 public int compare(StarSystemData o1, StarSystemData o2) {
446 return (
int) Math.signum(s2 - s1);
455 total += data.planets.size() * 1f;
456 total += data.habitable.size() * 2f;
457 total += data.resourceRich.size() * 0.25f;
462 public static CustomCampaignEntityAPI
addBeacon(StarSystemAPI system, RemnantSystemType type) {
464 SectorEntityToken anchor = system.getHyperspaceAnchor();
465 List<SectorEntityToken> points =
Global.
getSector().getHyperspace().getEntities(JumpPointAPI.class);
467 float minRange = 600;
469 float closestRange = Float.MAX_VALUE;
470 JumpPointAPI closestPoint =
null;
471 for (SectorEntityToken entity : points) {
472 JumpPointAPI point = (JumpPointAPI) entity;
474 if (point.getDestinations().isEmpty())
continue;
476 JumpDestination dest = point.getDestinations().get(0);
477 if (dest.getDestination().getContainingLocation() != system)
continue;
479 float dist = Misc.getDistance(anchor.getLocation(), point.getLocation());
480 if (dist < minRange + point.getRadius())
continue;
482 if (dist < closestRange) {
483 closestPoint = point;
488 CustomCampaignEntityAPI beacon =
Global.
getSector().getHyperspace().addCustomEntity(
null,
null, Entities.WARNING_BEACON, Factions.NEUTRAL);
490 beacon.getMemoryWithoutUpdate().set(type.getBeaconFlag(),
true);
493 case DESTROYED: beacon.addTag(Tags.BEACON_LOW);
break;
494 case SUPPRESSED: beacon.addTag(Tags.BEACON_MEDIUM);
break;
495 case RESURGENT: beacon.addTag(Tags.BEACON_HIGH);
break;
498 if (closestPoint ==
null) {
504 float angle = Misc.getAngleInDegrees(anchor.getLocation(), closestPoint.getLocation()) + angleOffset;
505 float radius = closestRange;
507 if (closestPoint.getOrbit() !=
null) {
511 closestPoint.getOrbit().getOrbitalPeriod());
512 beacon.setOrbit(orbit);
514 Vector2f beaconLoc = Misc.getUnitVectorAtDegreeAngle(angle);
515 beaconLoc.scale(radius);
516 Vector2f.add(beaconLoc, anchor.getLocation(), beaconLoc);
517 beacon.getLocation().set(beaconLoc);
521 Color glowColor =
new Color(255,200,0,255);
522 Color pingColor =
new Color(255,200,0,255);
523 if (type == RemnantSystemType.SUPPRESSED) {
524 glowColor =
new Color(250,155,0,255);
525 pingColor =
new Color(250,155,0,255);
526 }
else if (type == RemnantSystemType.RESURGENT) {
527 glowColor =
new Color(250,55,0,255);
528 pingColor =
new Color(250,125,0,255);
530 Misc.setWarningBeaconColors(beacon, glowColor, pingColor);
557 List<Constellation> constellations =
new ArrayList<Constellation>();
562 constellations.add(c);
565 if (exclude !=
null) {
566 constellations.removeAll(exclude);
569 Collections.sort(constellations,
new Comparator<Constellation>() {
571 float d1 = Misc.getDistance(o1.
getLocation(), sortFrom);
572 float d2 = Misc.getDistance(o2.
getLocation(), sortFrom);
573 return (
int) Math.signum(d2 - d1);
576 return constellations;
587 for (PlanetAPI p : system.getPlanets()) {
588 if (!p.isStar())
return false;
596 public List<CampaignFleetAPI>
addBattlestations(StarSystemData data,
float chanceToAddAny,
int min,
int max,
597 WeightedRandomPicker<String> stationTypes) {
598 List<CampaignFleetAPI> result =
new ArrayList<CampaignFleetAPI>();
599 if (
random.nextFloat() >= chanceToAddAny)
return result;
601 int num = min +
random.nextInt(max - min + 1);
602 if (
DEBUG) System.out.println(
" Adding " + num +
" battlestations");
603 for (
int i = 0; i < num; i++) {
607 String type = stationTypes.pick();
610 CampaignFleetAPI fleet = FleetFactoryV3.createEmptyFleet(Factions.REMNANTS, FleetTypes.BATTLESTATION,
null);
613 fleet.getFleetData().addFleetMember(member);
616 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE,
true);
617 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_NO_JUMP,
true);
618 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_ALLOW_DISENGAGE,
true);
619 fleet.addTag(Tags.NEUTRINO_HIGH);
621 fleet.setStationMode(
true);
625 data.system.addEntity(fleet);
628 fleet.clearAbilities();
629 fleet.addAbility(Abilities.TRANSPONDER);
630 fleet.getAbility(Abilities.TRANSPONDER).activate();
631 fleet.getDetectedRangeMod().modifyFlat(
"gen", 1000f);
638 boolean damaged = type.toLowerCase().contains(
"damaged");
639 String coreId = Commodities.ALPHA_CORE;
643 fleet.getMemoryWithoutUpdate().set(
"$damagedStation",
true);
644 fleet.setName(fleet.getName() +
" (Damaged)");
647 AICoreOfficerPlugin plugin = Misc.getAICoreOfficerPlugin(coreId);
648 PersonAPI commander = plugin.createPerson(coreId, fleet.getFaction().getId(),
random);
650 fleet.setCommander(commander);
651 fleet.getFlagship().setCaptain(commander);
658 member.getRepairTracker().setCR(member.getRepairTracker().getMaxCR());
680 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE_GEN,
681 new RemnantStationInteractionConfigGen());
691 public static class RemnantStationInteractionConfigGen
implements FIDConfigGen {
692 public FIDConfig createConfig() {
693 FIDConfig config =
new FIDConfig();
695 config.alwaysAttackVsAttack =
true;
696 config.leaveAlwaysAvailable =
true;
697 config.showFleetAttitude =
false;
698 config.showTransponderStatus =
false;
699 config.showEngageText =
false;
702 config.delegate =
new BaseFIDDelegate() {
703 public void postPlayerSalvageGeneration(InteractionDialogAPI dialog,
FleetEncounterContext context, CargoAPI salvage) {
704 new RemnantFleetInteractionConfigGen().createConfig().delegate.
705 postPlayerSalvageGeneration(dialog, context, salvage);
707 public void notifyLeave(InteractionDialogAPI dialog) {
709 public void battleContextCreated(InteractionDialogAPI dialog, BattleCreationContext bcc) {
710 bcc.aiRetreatAllowed =
false;
711 bcc.objectivesAllowed =
false;
static FactoryAPI getFactory()
static SectorAPI getSector()
List< StarSystemAPI > getSystems()
static boolean isNameSpecial(StarSystemAPI system)
void assignSpecialNames(NamingTreeNode curr)
static float getNormalRandom(float min, float max)
void addShipGraveyard(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > factions)
static StarSystemData computeSystemData(StarSystemAPI system)
static AddedEntity setEntityLocation(SectorEntityToken entity, EntityLocation loc, String type)
static EntityLocation pickCommonLocation(Random random, StarSystemAPI system, float gap, boolean allowStarOrbit, Set< SectorEntityToken > exclude)
void addResearchStations(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > stationTypes)
List< AddedEntity > addObjectives(StarSystemData data, float prob)
void addCaches(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > cacheTypes)
void addDerelictShips(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > factions)
static void convertOrbitWithSpin(SectorEntityToken entity, float spin)
AddedEntity addInactiveGate(StarSystemData data, float prob, float probDebris, float probShips, WeightedRandomPicker< String > factions)
WeightedRandomPicker< String > createStringPicker(Object ... params)
void addHabCenters(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > stationTypes)
void addDebrisFields(StarSystemData data, float chanceToAddAny, int min, int max)
AddedEntity addStation(EntityLocation loc, StarSystemData data, String customEntityId, String factionId)
void addMiningStations(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > stationTypes)
static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, int numSkills, Random random)
static void integrateAndAdaptCoreForAIFleet(FleetMemberAPI member)
static final int MAX_CONSTELLATIONS_WITH_REMNANTS
static float CONSTELLATION_SKIP_PROB
static boolean systemIsEmpty(StarSystemAPI system)
float getMainCenterScore(StarSystemData data)
void generateForSector(ThemeGenContext context, float allowedUnusedFraction)
List< StarSystemData > getSortedSystemsSuitedToBePopulated(List< StarSystemData > systems)
void addDefenders(List< StarSystemData > systemData)
static void addRemnantStationInteractionConfig(CampaignFleetAPI fleet)
List< CampaignFleetAPI > addBattlestations(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker< String > stationTypes)
static final int MIN_CONSTELLATIONS_WITH_REMNANTS
static boolean constellationIsEmpty(Constellation c)
void populateMain(StarSystemData data, RemnantSystemType type)
List< Constellation > getSortedAvailableConstellations(ThemeGenContext context, boolean emptyOk, final Vector2f sortFrom, List< Constellation > exclude)
void populateNonMain(StarSystemData data)
static CustomCampaignEntityAPI addBeacon(StarSystemAPI system, RemnantSystemType type)
static void assignSpecials(SectorEntityToken entity)
List< Constellation > constellations
Map< Constellation, String > majorThemes
static final String REMNANTS
OrbitAPI createCircularOrbitPointingDown(SectorEntityToken focus, float angle, float orbitRadius, float orbitDays)
FleetMemberAPI createFleetMember(FleetMemberType type, String variantOrWingId)