001package com.fs.starfarer.api.impl.campaign.procgen.themes; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.List; 008 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.AICoreOfficerPlugin; 013import com.fs.starfarer.api.campaign.CampaignFleetAPI; 014import com.fs.starfarer.api.campaign.CargoAPI; 015import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 016import com.fs.starfarer.api.campaign.InteractionDialogAPI; 017import com.fs.starfarer.api.campaign.JumpPointAPI; 018import com.fs.starfarer.api.campaign.JumpPointAPI.JumpDestination; 019import com.fs.starfarer.api.campaign.OrbitAPI; 020import com.fs.starfarer.api.campaign.PlanetAPI; 021import com.fs.starfarer.api.campaign.SectorEntityToken; 022import com.fs.starfarer.api.campaign.StarSystemAPI; 023import com.fs.starfarer.api.characters.PersonAPI; 024import com.fs.starfarer.api.combat.BattleCreationContext; 025import com.fs.starfarer.api.fleet.FleetMemberAPI; 026import com.fs.starfarer.api.fleet.FleetMemberType; 027import com.fs.starfarer.api.impl.campaign.FleetEncounterContext; 028import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.BaseFIDDelegate; 029import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.FIDConfig; 030import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.FIDConfigGen; 031import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 032import com.fs.starfarer.api.impl.campaign.ids.Abilities; 033import com.fs.starfarer.api.impl.campaign.ids.Commodities; 034import com.fs.starfarer.api.impl.campaign.ids.Entities; 035import com.fs.starfarer.api.impl.campaign.ids.Factions; 036import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 037import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 038import com.fs.starfarer.api.impl.campaign.ids.Tags; 039import com.fs.starfarer.api.impl.campaign.procgen.Constellation; 040import com.fs.starfarer.api.impl.campaign.procgen.DefenderDataOverride; 041import com.fs.starfarer.api.impl.campaign.procgen.NameAssigner; 042import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator; 043import com.fs.starfarer.api.impl.campaign.procgen.themes.RemnantSeededFleetManager.RemnantFleetInteractionConfigGen; 044import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner.SpecialCreationContext; 045import com.fs.starfarer.api.util.Misc; 046import com.fs.starfarer.api.util.WeightedRandomPicker; 047 048 049public class RemnantThemeGenerator extends BaseThemeGenerator { 050 051 public static enum RemnantSystemType { 052 DESTROYED(Tags.THEME_REMNANT_DESTROYED, "$remnantDestroyed"), 053 SUPPRESSED(Tags.THEME_REMNANT_SUPPRESSED, "$remnantSuppressed"), 054 RESURGENT(Tags.THEME_REMNANT_RESURGENT, "$remnantResurgent"), 055 ; 056 057 private String tag; 058 private String beaconFlag; 059 private RemnantSystemType(String tag, String beaconFlag) { 060 this.tag = tag; 061 this.beaconFlag = beaconFlag; 062 } 063 public String getTag() { 064 return tag; 065 } 066 public String getBeaconFlag() { 067 return beaconFlag; 068 } 069 } 070 071 072 public static final int MIN_CONSTELLATIONS_WITH_REMNANTS = 15; 073 public static final int MAX_CONSTELLATIONS_WITH_REMNANTS = 25; 074 075 public static float CONSTELLATION_SKIP_PROB = 0.25f; 076 077 078 public String getThemeId() { 079 return Themes.REMNANTS; 080 } 081 082 @Override 083 public void generateForSector(ThemeGenContext context, float allowedUnusedFraction) { 084 085 float total = (float) (context.constellations.size() - context.majorThemes.size()) * allowedUnusedFraction; 086 if (total <= 0) return; 087 088 int num = (int) StarSystemGenerator.getNormalRandom(MIN_CONSTELLATIONS_WITH_REMNANTS, MAX_CONSTELLATIONS_WITH_REMNANTS); 089 //num = 30; 090 if (num > total) num = (int) total; 091 092 093 int numDestroyed = (int) (num * (0.23f + 0.1f * random.nextFloat())); 094 if (numDestroyed < 1) numDestroyed = 1; 095 int numSuppressed = (int) (num * (0.23f + 0.1f * random.nextFloat())); 096 if (numSuppressed < 1) numSuppressed = 1; 097 098 float suppressedStationMult = 0.5f; 099 int suppressedStations = (int) Math.ceil(numSuppressed * suppressedStationMult); 100 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); 105 } else { 106 addSuppressedStation.add(false, 1f); 107 } 108 } 109 110 List<Constellation> constellations = getSortedAvailableConstellations(context, false, new Vector2f(), null); 111 Collections.reverse(constellations); 112 113 float skipProb = CONSTELLATION_SKIP_PROB; 114 if (total < num / (1f - skipProb)) { 115 skipProb = 1f - (num / total); 116 } 117 //skipProb = 0f; 118 119 List<StarSystemData> remnantSystems = new ArrayList<StarSystemData>(); 120 121 if (DEBUG) System.out.println("\n\n\n"); 122 if (DEBUG) System.out.println("Generating remnant systems"); 123 124 int count = 0; 125 126 int numUsed = 0; 127 for (int i = 0; i < num && i < constellations.size(); i++) { 128 Constellation c = constellations.get(i); 129 if (random.nextFloat() < skipProb) { 130 if (DEBUG) System.out.println("Skipping constellation " + c.getName()); 131 continue; 132 } 133 134 135 List<StarSystemData> systems = new ArrayList<StarSystemData>(); 136 for (StarSystemAPI system : c.getSystems()) { 137 StarSystemData data = computeSystemData(system); 138 systems.add(data); 139 } 140 141 List<StarSystemData> mainCandidates = getSortedSystemsSuitedToBePopulated(systems); 142 143 int numMain = 1 + random.nextInt(2); 144 if (numMain > mainCandidates.size()) numMain = mainCandidates.size(); 145 if (numMain <= 0) { 146 if (DEBUG) System.out.println("Skipping constellation " + c.getName() + ", no suitable main candidates"); 147 continue; 148 } 149 150 RemnantSystemType type = RemnantSystemType.RESURGENT; 151 if (numUsed < numDestroyed) { 152 type = RemnantSystemType.DESTROYED; 153 } else if (numUsed < numDestroyed + numSuppressed) { 154 type = RemnantSystemType.SUPPRESSED; 155 } 156 157// if (type == RemnantSystemType.RESURGENT) { 158// System.out.println("ewfwefwefwe"); 159// } 160 context.majorThemes.put(c, Themes.REMNANTS); 161 numUsed++; 162 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); 166 populateMain(data, type); 167 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); 172 } 173 data.system.addTag(Tags.THEME_REMNANT_MAIN); 174 data.system.addTag(type.getTag()); 175 remnantSystems.add(data); 176 177 if (!NameAssigner.isNameSpecial(data.system)) { 178 NameAssigner.assignSpecialNames(data.system); 179 } 180 181 182 if (type == RemnantSystemType.DESTROYED) { 183 RemnantSeededFleetManager fleets = new RemnantSeededFleetManager(data.system, 3, 8, 1, 2, 0.05f); 184 data.system.addScript(fleets); 185 } else if (type == RemnantSystemType.SUPPRESSED) { 186 RemnantSeededFleetManager fleets = new RemnantSeededFleetManager(data.system, 7, 12, 4, 12, 0.25f); 187 data.system.addScript(fleets); 188 189 Boolean addStation = random.nextFloat() < suppressedStationMult; 190 if (j == 0 && !addSuppressedStation.isEmpty()) addSuppressedStation.pickAndRemove(); 191 if (addStation) { 192 List<CampaignFleetAPI> stations = addBattlestations(data, 1f, 1, 1, createStringPicker("remnant_station2_Damaged", 10f)); 193 for (CampaignFleetAPI station : stations) { 194 int maxFleets = 2 + random.nextInt(3); 195 RemnantStationFleetManager activeFleets = new RemnantStationFleetManager( 196 station, 1f, 0, maxFleets, 25f, 6, 12); 197 data.system.addScript(activeFleets); 198 } 199 200 } 201 } else if (type == RemnantSystemType.RESURGENT) { 202 List<CampaignFleetAPI> stations = addBattlestations(data, 1f, 1, 1, createStringPicker("remnant_station2_Standard", 10f)); 203 for (CampaignFleetAPI station : stations) { 204 int maxFleets = 8 + random.nextInt(5); 205 RemnantStationFleetManager activeFleets = new RemnantStationFleetManager( 206 station, 1f, 0, maxFleets, 15f, 8, 24); 207 data.system.addScript(activeFleets); 208 } 209 } 210 } 211 212 for (StarSystemData data : systems) { 213 int index = mainCandidates.indexOf(data); 214 if (index >= 0 && index < numMain) continue; 215 216 populateNonMain(data); 217 218 if (type == RemnantSystemType.DESTROYED) { 219 data.system.addTag(Tags.THEME_INTERESTING_MINOR); 220 } else { 221 data.system.addTag(Tags.THEME_INTERESTING); 222 } 223 data.system.addTag(Tags.THEME_REMNANT); 224 //data.system.addTag(Tags.THEME_UNSAFE); // just a few 1-2 frigate fleets, and not even always 225 data.system.addTag(Tags.THEME_REMNANT_SECONDARY); 226 data.system.addTag(type.getTag()); 227 remnantSystems.add(data); 228 229 if (random.nextFloat() < 0.5f) { 230 RemnantSeededFleetManager fleets = new RemnantSeededFleetManager(data.system, 1, 3, 1, 2, 0.05f); 231 data.system.addScript(fleets); 232 } else { 233 data.system.addTag(Tags.THEME_REMNANT_NO_FLEETS); 234 } 235 } 236 237// if (count == 18) { 238// System.out.println("REM RANDOM INDEX " + count + ": " + random.nextLong()); 239// } 240 count++; 241 } 242 243 SpecialCreationContext specialContext = new SpecialCreationContext(); 244 specialContext.themeId = getThemeId(); 245 SalvageSpecialAssigner.assignSpecials(remnantSystems, specialContext); 246 247 addDefenders(remnantSystems); 248 249 if (DEBUG) System.out.println("Finished generating remnant systems\n\n\n\n\n"); 250 251 } 252 253 254 255 public void addDefenders(List<StarSystemData> systemData) { 256 for (StarSystemData data : systemData) { 257// float prob = 0.1f; 258// float max = 3f; 259// if (data.system.hasTag(Tags.THEME_REMNANT_SECONDARY)) { 260// prob = 0.05f; 261// max = 1f; 262// } 263 float mult = 1f; 264 if (data.system.hasTag(Tags.THEME_REMNANT_SECONDARY)) { 265 mult = 0.5f; 266 } 267 268 for (AddedEntity added : data.generated) { 269 if (added.entityType == null) continue; 270 if (Entities.WRECK.equals(added.entityType)) continue; 271 272 float prob = 0f; 273 float min = 1f; 274 float max = 1f; 275 if (Entities.STATION_MINING_REMNANT.equals(added.entityType)) { 276 prob = 0.25f; 277 min = 8; 278 max = 15; 279 } else if (Entities.ORBITAL_HABITAT_REMNANT.equals(added.entityType)) { 280 prob = 0.25f; 281 min = 8; 282 max = 15; 283 } else if (Entities.STATION_RESEARCH_REMNANT.equals(added.entityType)) { 284 prob = 0.25f; 285 min = 10; 286 max = 20; 287 } 288 // to compensate for this being changed to use fleet points 289 min *= 3; 290 max *= 3; 291 292 prob *= mult; 293 min *= mult; 294 max *= mult; 295 if (min < 1) min = 1; 296 if (max < 1) max = 1; 297 298 if (random.nextFloat() < prob) { 299 Misc.setDefenderOverride(added.entity, new DefenderDataOverride(Factions.REMNANTS, 1f, min, max, 4)); 300 } 301 //Misc.setDefenderOverride(added.entity, new DefenderDataOverride(Factions.REMNANTS, prob, 1, max)); 302 } 303 } 304 305 } 306 307 public void populateNonMain(StarSystemData data) { 308 if (DEBUG) System.out.println(" Generating secondary remnant system in " + data.system.getName()); 309 boolean special = data.isBlackHole() || data.isNebula() || data.isPulsar(); 310 if (special) { 311 addResearchStations(data, 0.75f, 1, 1, createStringPicker(Entities.STATION_RESEARCH_REMNANT, 10f)); 312 } 313 314 if (random.nextFloat() < 0.5f) return; 315 316 if (!data.resourceRich.isEmpty()) { 317 addMiningStations(data, 0.5f, 1, 1, createStringPicker(Entities.STATION_MINING_REMNANT, 10f)); 318 } 319 320 if (!special && !data.habitable.isEmpty()) { 321 // ruins on planet, or orbital station 322 addHabCenters(data, 0.25f, 1, 1, createStringPicker(Entities.ORBITAL_HABITAT_REMNANT, 10f)); 323 } 324 325 326 addShipGraveyard(data, 0.05f, 1, 1, 327 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f)); 328 329 //addDebrisFields(data, 0.25f, 1, 2, Factions.REMNANTS, 0.1f, 1, 1); 330 addDebrisFields(data, 0.25f, 1, 2); 331 332 addDerelictShips(data, 0.5f, 0, 3, 333 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f)); 334 335 addCaches(data, 0.25f, 0, 2, createStringPicker( 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 342 )); 343 344 } 345 346 347 public void populateMain(StarSystemData data, RemnantSystemType type) { 348 349 if (DEBUG) System.out.println(" Generating remnant center in " + data.system.getName()); 350 351 StarSystemAPI system = data.system; 352 353 addBeacon(system, type); 354 355 if (DEBUG) System.out.println(" Added warning beacon"); 356 357 int maxHabCenters = 1 + random.nextInt(3); 358 359 HabitationLevel level = HabitationLevel.LOW; 360 if (maxHabCenters == 2) level = HabitationLevel.MEDIUM; 361 if (maxHabCenters >= 3) level = HabitationLevel.HIGH; 362 363 addHabCenters(data, 1, maxHabCenters, maxHabCenters, createStringPicker(Entities.ORBITAL_HABITAT_REMNANT, 10f)); 364 365 // add various stations, orbiting entities, etc 366 float probGate = 1f; 367 float probRelay = 1f; 368 float probMining = 0.5f; 369 float probResearch = 0.25f; 370 371 switch (level) { 372 case HIGH: 373 probGate = 0.5f; 374 probRelay = 1f; 375 break; 376 case MEDIUM: 377 probGate = 0.3f; 378 probRelay = 0.75f; 379 break; 380 case LOW: 381 probGate = 0.2f; 382 probRelay = 0.5f; 383 break; 384 } 385 386 //addCommRelay(data, probRelay); 387 addObjectives(data, probRelay); 388 addInactiveGate(data, probGate, 0.5f, 0.5f, 389 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f)); 390 391 addShipGraveyard(data, 0.25f, 1, 1, 392 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f)); 393 394 addMiningStations(data, probMining, 1, 1, createStringPicker(Entities.STATION_MINING_REMNANT, 10f)); 395 396 addResearchStations(data, probResearch, 1, 1, createStringPicker(Entities.STATION_RESEARCH_REMNANT, 10f)); 397 398 399 //addDebrisFields(data, 0.75f, 1, 5, Factions.REMNANTS, 0.2f, 1, 3); 400 addDebrisFields(data, 0.75f, 1, 5); 401 402 403// MN-6186477243757813340 404// float test = Misc.getDistance(data.system.getLocation(), new Vector2f(-33500, 9000)); 405// if (test < 600) { 406// System.out.println("HERE"); 407// } 408 409 addDerelictShips(data, 0.75f, 0, 7, 410 createStringPicker(Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f)); 411 412 addCaches(data, 0.75f, 0, 3, createStringPicker( 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 419 )); 420 421 } 422 423 424 425 public List<StarSystemData> getSortedSystemsSuitedToBePopulated(List<StarSystemData> systems) { 426 List<StarSystemData> result = new ArrayList<StarSystemData>(); 427 428 for (StarSystemData data : systems) { 429 if (data.isBlackHole() || data.isNebula() || data.isPulsar()) continue; 430 431 if (data.planets.size() >= 3 || data.habitable.size() >= 1) { 432 result.add(data); 433 434// Collections.sort(data.habitable, new Comparator<PlanetAPI>() { 435// public int compare(PlanetAPI o1, PlanetAPI o2) { 436// return (int) Math.signum(o1.getMarket().getHazardValue() - o2.getMarket().getHazardValue()); 437// } 438// }); 439 } 440 } 441 442 Collections.sort(systems, new Comparator<StarSystemData>() { 443 public int compare(StarSystemData o1, StarSystemData o2) { 444 float s1 = getMainCenterScore(o1); 445 float s2 = getMainCenterScore(o2); 446 return (int) Math.signum(s2 - s1); 447 } 448 }); 449 450 return result; 451 } 452 453 public float getMainCenterScore(StarSystemData data) { 454 float total = 0f; 455 total += data.planets.size() * 1f; 456 total += data.habitable.size() * 2f; 457 total += data.resourceRich.size() * 0.25f; 458 return total; 459 } 460 461 462 public static CustomCampaignEntityAPI addBeacon(StarSystemAPI system, RemnantSystemType type) { 463 464 SectorEntityToken anchor = system.getHyperspaceAnchor(); 465 List<SectorEntityToken> points = Global.getSector().getHyperspace().getEntities(JumpPointAPI.class); 466 467 float minRange = 600; 468 469 float closestRange = Float.MAX_VALUE; 470 JumpPointAPI closestPoint = null; 471 for (SectorEntityToken entity : points) { 472 JumpPointAPI point = (JumpPointAPI) entity; 473 474 if (point.getDestinations().isEmpty()) continue; 475 476 JumpDestination dest = point.getDestinations().get(0); 477 if (dest.getDestination().getContainingLocation() != system) continue; 478 479 float dist = Misc.getDistance(anchor.getLocation(), point.getLocation()); 480 if (dist < minRange + point.getRadius()) continue; 481 482 if (dist < closestRange) { 483 closestPoint = point; 484 closestRange = dist; 485 } 486 } 487 488 CustomCampaignEntityAPI beacon = Global.getSector().getHyperspace().addCustomEntity(null, null, Entities.WARNING_BEACON, Factions.NEUTRAL); 489 //beacon.getMemoryWithoutUpdate().set("$remnant", true); 490 beacon.getMemoryWithoutUpdate().set(type.getBeaconFlag(), true); 491 492 switch (type) { 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; 496 } 497 498 if (closestPoint == null) { 499 float orbitDays = minRange / (10f + StarSystemGenerator.random.nextFloat() * 5f); 500 //beacon.setCircularOrbit(anchor, StarSystemGenerator.random.nextFloat() * 360f, minRange, orbitDays); 501 beacon.setCircularOrbitPointingDown(anchor, StarSystemGenerator.random.nextFloat() * 360f, minRange, orbitDays); 502 } else { 503 float angleOffset = 20f + StarSystemGenerator.random.nextFloat() * 20f; 504 float angle = Misc.getAngleInDegrees(anchor.getLocation(), closestPoint.getLocation()) + angleOffset; 505 float radius = closestRange; 506 507 if (closestPoint.getOrbit() != null) { 508// OrbitAPI orbit = Global.getFactory().createCircularOrbit(anchor, angle, radius, 509// closestPoint.getOrbit().getOrbitalPeriod()); 510 OrbitAPI orbit = Global.getFactory().createCircularOrbitPointingDown(anchor, angle, radius, 511 closestPoint.getOrbit().getOrbitalPeriod()); 512 beacon.setOrbit(orbit); 513 } else { 514 Vector2f beaconLoc = Misc.getUnitVectorAtDegreeAngle(angle); 515 beaconLoc.scale(radius); 516 Vector2f.add(beaconLoc, anchor.getLocation(), beaconLoc); 517 beacon.getLocation().set(beaconLoc); 518 } 519 } 520 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); 529 } 530 Misc.setWarningBeaconColors(beacon, glowColor, pingColor); 531 532 533 return beacon; 534 } 535 536// Map<LocationType, Float> weights = new HashMap<LocationType, Float>(); 537// weights.put(LocationType.PLANET_ORBIT, 10f); 538// weights.put(LocationType.JUMP_ORBIT, 1f); 539// weights.put(LocationType.NEAR_STAR, 1f); 540// weights.put(LocationType.OUTER_SYSTEM, 5f); 541// weights.put(LocationType.IN_ASTEROID_BELT, 10f); 542// weights.put(LocationType.IN_RING, 10f); 543// weights.put(LocationType.IN_ASTEROID_FIELD, 10f); 544// weights.put(LocationType.STAR_ORBIT, 1f); 545// weights.put(LocationType.IN_SMALL_NEBULA, 1f); 546// weights.put(LocationType.L_POINT, 1f); 547// WeightedRandomPicker<EntityLocation> locs = getLocations(system, 100f, weights); 548 549 550 /** 551 * Sorted by *descending* distance from sortFrom. 552 * @param context 553 * @param sortFrom 554 * @return 555 */ 556 protected List<Constellation> getSortedAvailableConstellations(ThemeGenContext context, boolean emptyOk, final Vector2f sortFrom, List<Constellation> exclude) { 557 List<Constellation> constellations = new ArrayList<Constellation>(); 558 for (Constellation c : context.constellations) { 559 if (context.majorThemes.containsKey(c)) continue; 560 if (!emptyOk && constellationIsEmpty(c)) continue; 561 562 constellations.add(c); 563 } 564 565 if (exclude != null) { 566 constellations.removeAll(exclude); 567 } 568 569 Collections.sort(constellations, new Comparator<Constellation>() { 570 public int compare(Constellation o1, Constellation o2) { 571 float d1 = Misc.getDistance(o1.getLocation(), sortFrom); 572 float d2 = Misc.getDistance(o2.getLocation(), sortFrom); 573 return (int) Math.signum(d2 - d1); 574 } 575 }); 576 return constellations; 577 } 578 579 580 public static boolean constellationIsEmpty(Constellation c) { 581 for (StarSystemAPI s : c.getSystems()) { 582 if (!systemIsEmpty(s)) return false; 583 } 584 return true; 585 } 586 public static boolean systemIsEmpty(StarSystemAPI system) { 587 for (PlanetAPI p : system.getPlanets()) { 588 if (!p.isStar()) return false; 589 } 590 //system.getTerrainCopy().isEmpty() 591 return true; 592 } 593 594 595 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; 600 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++) { 604 605 EntityLocation loc = pickCommonLocation(random, data.system, 200f, true, null); 606 607 String type = stationTypes.pick(); 608 if (loc != null) { 609 610 CampaignFleetAPI fleet = FleetFactoryV3.createEmptyFleet(Factions.REMNANTS, FleetTypes.BATTLESTATION, null); 611 612 FleetMemberAPI member = Global.getFactory().createFleetMember(FleetMemberType.SHIP, type); 613 fleet.getFleetData().addFleetMember(member); 614 615 //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE, true); 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); 620 621 fleet.setStationMode(true); 622 623 addRemnantStationInteractionConfig(fleet); 624 625 data.system.addEntity(fleet); 626 627 //fleet.setTransponderOn(true); 628 fleet.clearAbilities(); 629 fleet.addAbility(Abilities.TRANSPONDER); 630 fleet.getAbility(Abilities.TRANSPONDER).activate(); 631 fleet.getDetectedRangeMod().modifyFlat("gen", 1000f); 632 633 fleet.setAI(null); 634 635 setEntityLocation(fleet, loc, null); 636 convertOrbitWithSpin(fleet, 5f); 637 638 boolean damaged = type.toLowerCase().contains("damaged"); 639 String coreId = Commodities.ALPHA_CORE; 640 if (damaged) { 641 // alpha for both types; damaged is already weaker 642 //coreId = Commodities.BETA_CORE; 643 fleet.getMemoryWithoutUpdate().set("$damagedStation", true); 644 fleet.setName(fleet.getName() + " (Damaged)"); 645 } 646 647 AICoreOfficerPlugin plugin = Misc.getAICoreOfficerPlugin(coreId); 648 PersonAPI commander = plugin.createPerson(coreId, fleet.getFaction().getId(), random); 649 650 fleet.setCommander(commander); 651 fleet.getFlagship().setCaptain(commander); 652 653 if (!damaged) { 654 RemnantOfficerGeneratorPlugin.integrateAndAdaptCoreForAIFleet(fleet.getFlagship()); 655 RemnantOfficerGeneratorPlugin.addCommanderSkills(commander, fleet, null, 3, random); 656 } 657 658 member.getRepairTracker().setCR(member.getRepairTracker().getMaxCR()); 659 660 661 //RemnantSeededFleetManager.addRemnantAICoreDrops(random, fleet, mult); 662 663 result.add(fleet); 664 665// MarketAPI market = Global.getFactory().createMarket("station_market_" + fleet.getId(), fleet.getName(), 0); 666// market.setPrimaryEntity(fleet); 667// market.setFactionId(fleet.getFaction().getId()); 668// market.addCondition(Conditions.ABANDONED_STATION); 669// market.addSubmarket(Submarkets.SUBMARKET_STORAGE); 670// ((StoragePlugin)market.getSubmarket(Submarkets.SUBMARKET_STORAGE).getPlugin()).setPlayerPaidToUnlock(true); 671// fleet.setMarket(market); 672 673 } 674 } 675 676 return result; 677 } 678 679 public static void addRemnantStationInteractionConfig(CampaignFleetAPI fleet) { 680 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE_GEN, 681 new RemnantStationInteractionConfigGen()); 682 } 683 684 685 @Override 686 public int getOrder() { 687 return 1500; 688 } 689 690 691 public static class RemnantStationInteractionConfigGen implements FIDConfigGen { 692 public FIDConfig createConfig() { 693 FIDConfig config = new FIDConfig(); 694 695 config.alwaysAttackVsAttack = true; 696 config.leaveAlwaysAvailable = true; 697 config.showFleetAttitude = false; 698 config.showTransponderStatus = false; 699 config.showEngageText = false; 700 701 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); 706 } 707 public void notifyLeave(InteractionDialogAPI dialog) { 708 } 709 public void battleContextCreated(InteractionDialogAPI dialog, BattleCreationContext bcc) { 710 bcc.aiRetreatAllowed = false; 711 bcc.objectivesAllowed = false; 712 } 713 }; 714 return config; 715 } 716 } 717 718 719} 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735