001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.Collections;
006import java.util.Comparator;
007import java.util.HashSet;
008import java.util.List;
009import java.util.Set;
010
011import org.lwjgl.util.vector.Vector2f;
012
013import com.fs.starfarer.api.EveryFrameScript;
014import com.fs.starfarer.api.Global;
015import com.fs.starfarer.api.campaign.CampaignFleetAPI;
016import com.fs.starfarer.api.campaign.CargoAPI;
017import com.fs.starfarer.api.campaign.CustomEntitySpecAPI;
018import com.fs.starfarer.api.campaign.JumpPointAPI;
019import com.fs.starfarer.api.campaign.PlanetAPI;
020import com.fs.starfarer.api.campaign.SectorEntityToken;
021import com.fs.starfarer.api.campaign.StarSystemAPI;
022import com.fs.starfarer.api.fleet.FleetMemberAPI;
023import com.fs.starfarer.api.impl.campaign.CoronalTapParticleScript;
024import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin;
025import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData;
026import com.fs.starfarer.api.impl.campaign.econ.impl.PlanetaryShield;
027import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflater;
028import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams;
029import com.fs.starfarer.api.impl.campaign.ids.Commodities;
030import com.fs.starfarer.api.impl.campaign.ids.Conditions;
031import com.fs.starfarer.api.impl.campaign.ids.Entities;
032import com.fs.starfarer.api.impl.campaign.ids.Factions;
033import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
034import com.fs.starfarer.api.impl.campaign.ids.Planets;
035import com.fs.starfarer.api.impl.campaign.ids.StarTypes;
036import com.fs.starfarer.api.impl.campaign.ids.Submarkets;
037import com.fs.starfarer.api.impl.campaign.ids.Tags;
038import com.fs.starfarer.api.impl.campaign.ids.Terrain;
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.NameGenData;
042import com.fs.starfarer.api.impl.campaign.procgen.PlanetConditionGenerator;
043import com.fs.starfarer.api.impl.campaign.procgen.PlanetGenDataSpec;
044import com.fs.starfarer.api.impl.campaign.procgen.ProcgenUsedNames;
045import com.fs.starfarer.api.impl.campaign.procgen.ProcgenUsedNames.NamePick;
046import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
047import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator.StarSystemType;
048import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner.ShipRecoverySpecialCreator;
049import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner.SpecialCreationContext;
050import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.PerShipData;
051import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipCondition;
052import com.fs.starfarer.api.impl.campaign.terrain.AsteroidFieldTerrainPlugin.AsteroidFieldParams;
053import com.fs.starfarer.api.util.Misc;
054import com.fs.starfarer.api.util.WeightedRandomPicker;
055
056
057public class MiscellaneousThemeGenerator extends BaseThemeGenerator {
058        
059        public static String PK_SYSTEM_KEY = "$core_pkSystem";
060        public static String PK_PLANET_KEY = "$core_pkPlanet";
061        public static String PK_CACHE_KEY = "$core_pkCache";
062        public static String PK_NEXUS_KEY = "$core_pkNexus";
063        
064        public static String PLANETARY_SHIELD_PLANET_KEY = "$core_planetaryShieldPlanet";
065        public static String PLANETARY_SHIELD_PLANET = "$psi_planet";
066        
067        public static String LOCR_BLOCK_FIRST_SURVEY = "$locr_blockFirstSurvey";
068        public static String LOCR_LUDDIC_PLANET_KEY = "$locr_luddicPlanet";
069        public static String LOCR_LUDDIC_TRANSPORT_KEY = "$locr_luddicTransport";
070        public static String LOCR_LUDDIC = "$locr_luddic";
071        public static String LOCR_MINERS_PLANET_KEY = "$locr_minersPlanet";
072        public static String LOCR_MINERS = "$locr_miners";
073        //public static String LOCR_UTOPIA_PLANET_KEY = "$locr_utopiaPlanet";
074        //public static String LOCR_UTOPIA = "$locr_utopia";
075        public static String LOCR_PIRATE_PLANET_KEY = "$locr_piratePlanet";
076        public static String LOCR_PIRATE = "$locr_pirate";
077        
078        public static float PROB_TO_ADD_SOMETHING = 0.5f;
079        
080        public static int MIN_GATES = Global.getSettings().getInt("minNonCoreGatesInSector");
081        public static int MAX_GATES = Global.getSettings().getInt("maxNonCoreGatesInSector");
082        public static int MIN_GATES_TO_ADD = Global.getSettings().getInt("minGatesToAddOnSecondPass");
083        
084        
085        public String getThemeId() {
086                return Themes.MISC;
087        }
088
089        @Override
090        public float getWeight() {
091                return 0f;
092        }
093
094        @Override
095        public int getOrder() {
096                return 1000000;
097        }
098
099        @Override
100        public void generateForSector(ThemeGenContext context, float allowedUnusedFraction) {
101                
102                if (DEBUG) System.out.println("\n\n\n");
103                if (DEBUG) System.out.println("Generating misc derelicts etc in all systems");
104                //getSortedAvailableConstellations(context, true, new Vector2f(), null).size()
105                List<StarSystemData> all = new ArrayList<StarSystemData>();
106                
107                /* this adds misc stuff to systems that are:
108                        1) Not tagged with some other theme
109                        2) But does add misc stuff to derelict-tagged systems
110                        So, basicallly it covers:
111                                derelict theme + whatever few constellations didn't get anything from any theme
112                */
113                for (Constellation c : context.constellations) {
114                        String theme = context.majorThemes.get(c);
115                        
116                        List<StarSystemData> systems = new ArrayList<StarSystemData>();
117                        for (StarSystemAPI system : c.getSystems()) {
118                                StarSystemData data = computeSystemData(system);
119                                systems.add(data);
120                        }
121                        
122                        for (StarSystemData data  : systems) {
123                                //if (data.system.getName().toLowerCase().contains("alpha mok morred")) {
124//                              if (data.system.getName().toLowerCase().contains("vasuki")) {
125//                                      System.out.println("efwefwef");
126//                              }
127                                boolean derelict = data.system.hasTag(Tags.THEME_DERELICT);
128                                if (!derelict && theme != null && !data.system.getTags().isEmpty()) continue;
129//                              if (!derelict && theme != null && !theme.equals(Themes.DERELICTS) &&
130//                                              !theme.equals(Themes.NO_THEME)) continue;
131                                
132                                if (random.nextFloat() > PROB_TO_ADD_SOMETHING || (derelict && theme != null)) {
133                                        data.system.addTag(Tags.THEME_MISC_SKIP);
134                                        continue;
135                                }
136
137                                populateNonMain(data);
138                                all.add(data);
139                                data.system.addTag(Tags.THEME_MISC);
140                                data.system.addTag(Tags.THEME_INTERESTING_MINOR);
141                        }
142                }
143                
144                
145                SpecialCreationContext specialContext = new SpecialCreationContext();
146                specialContext.themeId = getThemeId();
147                SalvageSpecialAssigner.assignSpecials(all, specialContext);
148                
149                if (DEBUG) System.out.println("Finished generating misc derelicts\n\n\n\n\n");
150                
151                
152                addDerelicts(context, "legion_xiv_Elite", 2, 3, 1, 2, Tags.THEME_REMNANT);
153                addDerelicts(context, "phantom_Elite", 1, 2, 0, 1, Tags.THEME_REMNANT, Tags.THEME_RUINS, Tags.THEME_DERELICT, Tags.THEME_UNSAFE);
154                addDerelicts(context, "revenant_Elite", 1, 2, 0, 1, Tags.THEME_REMNANT, Tags.THEME_RUINS, Tags.THEME_DERELICT, Tags.THEME_UNSAFE);
155                
156                
157                addRedPlanet(context);
158                addPKSystem(context);
159                addSolarShadesAndMirrors(context);
160                
161                addCoronalTaps(context);
162                
163                addExtraGates(context);
164                
165                addLOCRPiratePlanet(context);
166                addLOCRLuddicPlanet(context);
167                //addLOCRUtopiaPlanet(context);
168                addLOCRMinersPlanet(context);
169        }
170        
171        protected void addRedPlanet(ThemeGenContext context) {
172                if (DEBUG) System.out.println("Looking for planetary shield planet");
173                
174                PlanetAPI bestHab = null;
175                PlanetAPI bestNonHab = null;
176//              OrbitGap gapHab = null;
177//              OrbitGap gapNonHab = null;
178                float habDist = 0;
179                float nonHabDist = 0;
180
181                // looking for a habitable planet furthest from the Sector's center, with a bit of 
182                // a random factor
183                int systemsChecked = 0;
184                for (Constellation c : context.constellations) {
185                        for (StarSystemAPI system : c.getSystems()) {
186                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
187                                
188                                if (!system.hasTag(Tags.THEME_MISC_SKIP) && 
189                                                !system.hasTag(Tags.THEME_MISC)) {
190                                        continue;
191                                }
192                                //[theme_derelict, theme_derelict_probes, theme_misc_skip, theme_derelict_survey_ship]
193                                if (system.hasTag(Tags.THEME_DERELICT)) {
194                                        continue;
195                                }
196                                
197                                systemsChecked++;
198                                
199                                for (PlanetAPI curr : system.getPlanets()) {
200                                        if (curr.isStar()) continue;
201                                        if (curr.isMoon()) continue;
202                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;
203                                        
204                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
205                                        
206                                        float dist = system.getLocation().length() + random.nextFloat() * 6000;
207                                        if (curr.getMarket().hasCondition(Conditions.HABITABLE)) {
208                                                if (dist > habDist) {
209//                                                      List<OrbitGap> gaps = findGaps(curr, 50f, 500f, 100f);
210//                                                      if (!gaps.isEmpty()) {
211                                                                habDist = dist;
212                                                                bestHab = curr;
213//                                                              gapHab = gaps.get(0);
214//                                                      }
215                                                }
216                                        } else {
217                                                if (dist > nonHabDist) {
218//                                                      List<OrbitGap> gaps = findGaps(curr, 50f, 500f, 100f);
219//                                                      if (!gaps.isEmpty()) {
220                                                                nonHabDist = dist;
221                                                                bestNonHab = curr;
222//                                                              gapNonHab = gaps.get(0);
223//                                                      }
224                                                }
225                                        }
226                                }
227                        }
228                }
229                
230                PlanetAPI planet = bestHab;
231                //OrbitGap gap = gapHab;
232                if (planet == null) {
233                        planet = bestNonHab;
234                        //gap = gapNonHab;
235                }
236
237                if (planet != null) {
238                        if (DEBUG) System.out.println("Adding Planetary Shield to [" + planet.getName() + "] in [" + planet.getContainingLocation().getNameWithLowercaseType() + "]");
239                        PlanetaryShield.applyVisuals(planet);
240                        Global.getSector().getMemoryWithoutUpdate().set(PLANETARY_SHIELD_PLANET_KEY, planet);
241                        planet.getMemoryWithoutUpdate().set(PLANETARY_SHIELD_PLANET, true);
242                        planet.getStarSystem().addTag(Tags.THEME_SPECIAL);
243                        
244                        long seed = StarSystemGenerator.random.nextLong();
245                        planet.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, seed);
246                        planet.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SPEC_ID_OVERRIDE, "red_planet");
247                        planet.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
248                        //planet.addTag(Tags.SALVAGEABLE);
249                        
250//                      SectorEntityToken beacon = Misc.addWarningBeacon(planet, gap, Tags.BEACON_HIGH);
251//                      beacon.getMemoryWithoutUpdate().set(PLANETARY_SHIELD_BEACON, true);
252//                      beacon.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, seed);
253                        
254                } else {
255                        if (DEBUG) System.out.println("Failed to find a planet in remnant systems");
256                }
257                if (DEBUG) System.out.println("Finished adding Planetary Shield planet\n\n\n\n\n");
258        }
259        
260        protected void addDerelicts(ThemeGenContext context, String variant,
261                                        int minNonSalvageable, int maxNonSalvageable, 
262                                        int minSalvageable, int maxSalvageable, 
263                                        String ... allowedThemes) {
264                if (Global.getSettings().getVariant(variant) != null) {
265                        if (DEBUG) System.out.println("Adding " + variant + " to star systems");
266                        
267                        Set<String> tags = new HashSet<String>(Arrays.asList(allowedThemes));
268                        
269                        int numSalvageable = minSalvageable + random.nextInt(maxSalvageable - minSalvageable + 1);
270                        int numNonSalvageable = minNonSalvageable + random.nextInt(maxNonSalvageable - minNonSalvageable + 1);
271                        
272                        List<Constellation> list = new ArrayList<Constellation>(context.constellations);
273                        Collections.shuffle(list, random);
274                        
275                        List<StarSystemData> systems = new ArrayList<StarSystemData>();
276                        for (Constellation c : list) {
277                                for (StarSystemAPI system : c.getSystems()) {
278                                        StarSystemData data = computeSystemData(system);
279                                        systems.add(data);
280                                }
281                        }
282                                
283                        Collections.shuffle(systems, random);
284                        for (StarSystemData data  : systems) {
285                                boolean matches = false;
286                                for (String tag : data.system.getTags()) {
287                                        if (tags.contains(tag)) {
288                                                matches = true;
289                                                break;
290                                        }
291                                }
292                                if (!matches) continue;
293
294                                EntityLocation loc = pickAnyLocation(random, data.system, 70f, null);
295                                AddedEntity ae = addDerelictShip(data, loc, variant);
296                                if (ae != null) {
297                                        if (numSalvageable > 0) {
298                                                numSalvageable--;
299                                                ShipRecoverySpecialCreator creator = new ShipRecoverySpecialCreator(random, 0, 0, false, null, null);
300                                                Object specialData = creator.createSpecial(ae.entity, new SpecialCreationContext());
301                                                if (specialData != null) {
302                                                        Misc.setSalvageSpecial(ae.entity, specialData);
303                                                }
304                                        } else {
305                                                numNonSalvageable--;
306                                                SalvageSpecialAssigner.assignSpecials(ae.entity, true);
307                                        }
308                                        if (DEBUG) System.out.println("      Added " + variant + " to " + data.system + "\n");
309                                }
310                                if (numSalvageable + numNonSalvageable <= 0) break;
311                        }
312                        //if (numSalvageable + numNonSalvageable <= 0) break;
313                        
314                        if (DEBUG) System.out.println("Finished adding " + variant + " to star systems\n\n\n\n\n");
315                }
316        }
317        
318        
319        protected void addSolarShadesAndMirrors(ThemeGenContext context) {
320                
321                int num = 2 + random.nextInt(3);
322                
323                //System.out.println("RANDOM CHECK: " + random.nextLong());
324                
325                if (DEBUG) System.out.println("Adding up to " + num + " solar shades and mirrors");
326                List<Constellation> list = new ArrayList<Constellation>(context.constellations);
327                WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>(random);
328                for (Constellation c : list) {
329                        for (StarSystemAPI system : c.getSystems()) {
330                                if (system.hasTag(Tags.THEME_CORE)) continue;
331                                if (system.isNebula()) continue;
332                                
333                                for (PlanetAPI planet : system.getPlanets()) {
334                                        if (planet.isStar()) continue;
335                                        
336                                        SectorEntityToken focus = planet.getOrbitFocus();
337                                        if (!(focus instanceof PlanetAPI)) continue;
338                                        if (!((PlanetAPI) focus).isNormalStar()) continue;
339                                        
340                                        
341                                        PlanetGenDataSpec spec = (PlanetGenDataSpec) 
342                                                                                        Global.getSettings().getSpec(PlanetGenDataSpec.class, planet.getSpec().getPlanetType(), true);
343                                        if (spec == null) continue;
344                                        
345                                        String cat = spec.getCategory();
346                                        if (cat == null) continue;
347                                        
348                                        float weight = 0f;
349                                        if (Planets.CAT_HAB1.equals(cat)) {
350                                                weight = 1f;
351                                        } else if (Planets.CAT_HAB2.equals(cat)) {
352                                                weight = 1f;
353                                        } else if (Planets.CAT_HAB3.equals(cat)) {
354                                                weight = 1f;
355                                        }
356                                        
357                                        if (weight <= 0) continue;
358                                        
359                                        weight = 0;
360                                        
361                                        if (planet.hasCondition(Conditions.HOT)) {
362                                                weight += 5f;
363                                        }
364                                        if (planet.hasCondition(Conditions.POOR_LIGHT)) {
365                                                weight += 5f;
366                                        }
367                                        if (planet.hasCondition(Conditions.WATER_SURFACE)) {
368                                                weight += 5f;
369                                        }
370                                        if (Misc.hasFarmland(planet.getMarket())) {
371                                                weight += 10f;
372                                        }
373                                        
374                                        if (weight <= 0) continue;
375                                        
376                                        // +250 beyond normal radius
377                                        boolean enoughRoom = true;
378                                        for (PlanetAPI other : system.getPlanets()) {
379                                                if (other.getOrbitFocus() == planet) {
380                                                        if (other.getCircularOrbitRadius() < planet.getRadius() + other.getRadius() + 320) {
381                                                                enoughRoom = false;
382                                                                break;
383                                                        }
384                                                }
385                                        }
386                                        if (!enoughRoom) continue;
387                                        
388                                        
389                                        picker.add(planet, weight);
390                                }
391                        }
392                }
393                
394                if (DEBUG) System.out.println("Found " + picker.getItems().size() + " candidates");
395                for (int i = 0; i < num && !picker.isEmpty(); i++) {
396                        PlanetAPI planet = picker.pickAndRemove();
397                        if (DEBUG) System.out.println("Adding solar shades and mirrors to [" + planet.getName() + "] in [" + 
398                                        planet.getStarSystem() + " located at " + planet.getLocationInHyperspace());
399                        
400                        planet.getMarket().addCondition(Conditions.SOLAR_ARRAY);
401                        
402                        StarSystemAPI system = planet.getStarSystem();
403                        PlanetAPI star = (PlanetAPI) planet.getOrbitFocus();
404                        
405                        boolean shade = planet.hasCondition(Conditions.HOT) ||
406                                                                planet.getTypeId().equals(Planets.DESERT) ||
407                                                                planet.getTypeId().equals(Planets.DESERT1) ||
408                                                                planet.getTypeId().equals(Planets.ARID) ||
409                                                                star.getTypeId().equals(StarTypes.BLUE_GIANT) ||
410                                                                star.getTypeId().equals(StarTypes.BLUE_SUPERGIANT);
411                        boolean mirror = planet.hasCondition(Conditions.POOR_LIGHT) ||
412                                                                planet.getTypeId().equals(Planets.PLANET_TERRAN_ECCENTRIC) ||
413//                                                              star.getTypeId().equals(StarTypes.RED_SUPERGIANT) ||
414//                                                              star.getTypeId().equals(StarTypes.RED_GIANT) ||
415                                                                star.getTypeId().equals(StarTypes.RED_DWARF) ||
416                                                                star.getTypeId().equals(StarTypes.BROWN_DWARF);
417                        
418                        boolean forceFew = false; 
419                        if (!shade && !mirror) {
420                                mirror = true;
421                                shade = true;
422                                forceFew = true;
423                        }
424                        
425                        String faction = Factions.NEUTRAL;
426                        float period = planet.getCircularOrbitPeriod();
427                        float angle = planet.getCircularOrbitAngle();
428                        float radius = 270f + planet.getRadius();
429                        //String name = planet.getName();
430                        
431                        float xp = 300f;
432                        float profile = 2000f;
433                        
434                        if (mirror) {
435                                boolean manyMirrors = random.nextBoolean();
436                                
437                                SectorEntityToken mirror2 = system.addCustomEntity(null, "Stellar Mirror Beta", Entities.STELLAR_MIRROR, faction);      
438                                SectorEntityToken mirror3 = system.addCustomEntity(null, "Stellar Mirror Gamma", Entities.STELLAR_MIRROR, faction);
439                                SectorEntityToken mirror4 = system.addCustomEntity(null, "Stellar Mirror Delta", Entities.STELLAR_MIRROR, faction);
440                                mirror2.setCircularOrbitPointingDown(planet, angle - 30, radius, period);       
441                                mirror3.setCircularOrbitPointingDown(planet, angle + 0, radius, period);        
442                                mirror4.setCircularOrbitPointingDown(planet, angle + 30, radius, period);
443                                makeDiscoverable(mirror2, xp, profile);
444                                makeDiscoverable(mirror3, xp, profile);
445                                makeDiscoverable(mirror4, xp, profile);
446                                
447                                if (manyMirrors && !forceFew) {
448                                        SectorEntityToken mirror1 = system.addCustomEntity(null, "Stellar Mirror Alpha", Entities.STELLAR_MIRROR, faction);
449                                        SectorEntityToken mirror5 = system.addCustomEntity(null, "Stellar Mirror Epsilon", Entities.STELLAR_MIRROR, faction);
450                                        mirror1.setCircularOrbitPointingDown(planet, angle - 60, radius, period);
451                                        mirror5.setCircularOrbitPointingDown(planet, angle + 60, radius, period);
452                                        makeDiscoverable(mirror1, xp, profile);
453                                        makeDiscoverable(mirror5, xp, profile);
454                                }
455                        }
456                        
457                        if (shade) {
458                                boolean manyShades = random.nextBoolean();
459                                SectorEntityToken shade2 = system.addCustomEntity(null, "Stellar Shade Psi", Entities.STELLAR_SHADE, faction);
460                                shade2.setCircularOrbitPointingDown(planet, angle + 180 + 0, radius + 25, period);
461                                makeDiscoverable(shade2, xp, profile);
462                                
463                                if (manyShades && !forceFew) {
464                                        SectorEntityToken shade1 = system.addCustomEntity(null, "Stellar Shade Omega", Entities.STELLAR_SHADE, faction);
465                                        SectorEntityToken shade3 = system.addCustomEntity(null, "Stellar Shade Chi", Entities.STELLAR_SHADE, faction);
466                                        shade1.setCircularOrbitPointingDown(planet, angle + 180 - 26, radius - 10, period);
467                                        shade3.setCircularOrbitPointingDown(planet, angle + 180 + 26, radius - 10, period);
468                                        makeDiscoverable(shade1, xp, profile);
469                                        makeDiscoverable(shade3, xp, profile);
470                                }
471                        }
472                        
473                }
474                
475                if (DEBUG) System.out.println("Done adding solar shades and mirrors");
476        }
477        
478        public static void makeDiscoverable(SectorEntityToken entity, float xp, float sensorProfile) {
479                entity.setDiscoverable(true);
480                entity.setDiscoveryXP(xp);
481                entity.setSensorProfile(sensorProfile);
482        }
483        
484        
485        public void populateNonMain(StarSystemData data) {
486                if (DEBUG) System.out.println(" Generating misc derelicts in system " + data.system.getName());
487                boolean special = data.isBlackHole() || data.isNebula() || data.isPulsar();
488                if (special) {
489                        addResearchStations(data, 0.25f, 1, 1, createStringPicker(Entities.STATION_RESEARCH, 10f));
490                }
491                
492                if (random.nextFloat() < 0.5f) return;
493                
494                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(random, data.system.getCenter(),
495                                                                                                                15f, 10f, 10f);
496                
497                addShipGraveyard(data, 0.05f, 1, 1, factions);
498                
499                addDebrisFields(data, 0.25f, 1, 2);
500
501                addDerelictShips(data, 0.5f, 0, 3, factions);
502                
503                addCaches(data, 0.25f, 0, 2, createStringPicker( 
504                                Entities.WEAPONS_CACHE, 4f,
505                                Entities.WEAPONS_CACHE_SMALL, 10f,
506                                Entities.WEAPONS_CACHE_HIGH, 4f,
507                                Entities.WEAPONS_CACHE_SMALL_HIGH, 10f,
508                                Entities.WEAPONS_CACHE_LOW, 4f,
509                                Entities.WEAPONS_CACHE_SMALL_LOW, 10f,
510                                Entities.SUPPLY_CACHE, 4f,
511                                Entities.SUPPLY_CACHE_SMALL, 10f,
512                                Entities.EQUIPMENT_CACHE, 4f,
513                                Entities.EQUIPMENT_CACHE_SMALL, 10f
514                                ));
515                
516        }
517        
518        
519        protected void addExtraGates(ThemeGenContext context) {
520//              List<SectorEntityToken> gates = new ArrayList<SectorEntityToken>();
521//              List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>();
522//              List<Constellation> list = new ArrayList<Constellation>(context.constellations);
523//              for (Constellation c : list) {
524//                      for (StarSystemAPI system : c.getSystems()) {
525//                              gates.addAll(system.getEntitiesWithTag(Tags.GATE));
526//                              systems.add(system);
527//                      }
528//              }
529
530                List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>(Global.getSector().getStarSystems());
531                List<SectorEntityToken> gates = new ArrayList<SectorEntityToken>();
532                
533                for (StarSystemAPI system : new ArrayList<StarSystemAPI>(systems)) {
534                        //if (system.hasTag(Tags.THEME_CORE)) continue; // this isn't set yet
535                        boolean galatia = system.getBaseName().toLowerCase().equals("galatia");
536                        if (system.getTags().isEmpty() || galatia) {
537                                systems.remove(system);
538                                continue;
539                        }
540                        gates.addAll(system.getEntitiesWithTag(Tags.GATE));
541                }
542                
543                
544                int addGates = MIN_GATES + random.nextInt(MAX_GATES - MIN_GATES + 1) - gates.size();
545                if (addGates < MIN_GATES_TO_ADD) addGates = MIN_GATES_TO_ADD;
546                if (addGates <= 0) {
547                        if (DEBUG) System.out.println("  Already have " + gates.size() + " gates, not adding any");
548                        return;
549                }
550                
551                List<StarSystemData> all = new ArrayList<BaseThemeGenerator.StarSystemData>();
552                
553                if (DEBUG) System.out.println("");
554                if (DEBUG) System.out.println("");
555                if (DEBUG) System.out.println("");
556                if (DEBUG) System.out.println("  Adding " + addGates + " extra gates, for a total of " + (addGates + gates.size()));
557                
558                for (int i = 0; i < addGates; i++) {
559                        float maxDist = 0;
560                        StarSystemAPI farthest = null;
561                        for (StarSystemAPI system : systems) {
562                                if (system.getPlanets().size() < 3) continue; // skip empty systems
563                                
564                                float minDist = Float.MAX_VALUE;
565                                for (SectorEntityToken gate : gates) {
566                                        float dist = Misc.getDistanceLY(gate, system.getCenter());
567                                        if (dist < minDist) minDist = dist;
568                                }
569                                if (minDist > maxDist) {
570                                        maxDist = minDist;
571                                        farthest = system;
572                                }
573                        }
574                        if (farthest != null) {
575                                StarSystemData data = new StarSystemData();
576                                data.system = farthest;
577                                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(random, farthest.getCenter(),
578                                                                                                                                                                15f, 5f, 5f);
579                                AddedEntity gate = addInactiveGate(data, 1f, 0.5f, 0.5f, factions);
580                                if (gate != null && gate.entity != null) gates.add(gate.entity);
581                        }
582                }
583                if (DEBUG) System.out.println("  Done adding extra gates");
584                if (DEBUG) System.out.println("");
585                if (DEBUG) System.out.println("");
586                if (DEBUG) System.out.println("");
587                
588                SpecialCreationContext specialContext = new SpecialCreationContext();
589                specialContext.themeId = getThemeId();
590                SalvageSpecialAssigner.assignSpecials(all, specialContext);
591                
592                
593                
594        }
595        
596        
597        protected void addCoronalTaps(ThemeGenContext context) {
598                if (DEBUG) System.out.println("Adding coronal taps...");
599                
600                List<Constellation> list = new ArrayList<Constellation>(context.constellations);
601                
602                WeightedRandomPicker<StarSystemAPI> tapSystems = new WeightedRandomPicker<StarSystemAPI>(StarSystemGenerator.random);
603                WeightedRandomPicker<StarSystemAPI> backup = new WeightedRandomPicker<StarSystemAPI>(StarSystemGenerator.random);
604                for (Constellation c : list) {
605                        for (StarSystemAPI system : c.getSystems()) {
606                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
607                                
608                                float w = 0f;
609                                if (system.hasTag(Tags.THEME_REMNANT)) {
610                                        w = 10f;
611                                } else if (system.hasTag(Tags.THEME_DERELICT)) {
612                                        w = 10f;
613                                } else if (system.hasTag(Tags.THEME_RUINS)) {
614                                        w = 10f;
615                                } else if (system.hasTag(Tags.THEME_MISC)) {
616                                        w = 5f;
617                                }
618        
619                                if (w <= 0) continue;
620        
621                                if (system.getType() == StarSystemType.TRINARY_2CLOSE) {
622                                        w *= 5f;
623                                }
624        
625                                boolean hasBlueStar = false;
626                                boolean hasNormalStar = false;
627                                for (PlanetAPI planet : system.getPlanets()) {
628                                        if (!planet.isNormalStar()) continue;
629                                        if (planet.getTypeId().equals(StarTypes.BLUE_GIANT) ||
630                                                        planet.getTypeId().equals(StarTypes.BLUE_SUPERGIANT)) { 
631                                                hasBlueStar = true;
632                                        }
633                                        hasNormalStar = true;
634                                }
635                                
636                                if (!hasNormalStar) continue;
637                                
638                                WeightedRandomPicker<StarSystemAPI> use = tapSystems;
639                                if (!hasBlueStar) {
640                                        use = backup;
641                                }
642                                use.add(system, w);
643                        }
644                }
645                
646                
647                if (tapSystems.isEmpty()) {
648                        tapSystems.addAll(backup);
649                }
650                
651                int numTaps = 2 + random.nextInt(2);
652                numTaps = 2;
653                int added = 0;
654                while (added < numTaps && !tapSystems.isEmpty()) {
655                        StarSystemAPI pick = tapSystems.pickAndRemove();
656                        AddedEntity tap = addCoronalTap(pick);
657                        if (tap != null) {
658                                added++;
659                        }
660                }
661                
662                if (DEBUG) System.out.println("Done adding coronal taps\n\n\n");
663        }
664        
665        public static class MakeCoronalTapFaceNearestStar implements EveryFrameScript {
666                protected SectorEntityToken tap;
667                public MakeCoronalTapFaceNearestStar(SectorEntityToken tap) {
668                        this.tap = tap;
669                }
670                public void advance(float amount) {
671                        if (!tap.isInCurrentLocation()) return;
672                        
673                        float minDist = Float.MAX_VALUE;
674                        PlanetAPI closest = null;
675                        for (PlanetAPI star : tap.getContainingLocation().getPlanets()) {
676                                if (!star.isStar()) continue;
677                                float dist = Misc.getDistance(tap.getLocation(), star.getLocation());
678                                if (dist < minDist) {
679                                        minDist = dist;
680                                        closest = star;
681                                }
682                        }
683                        if (closest != null) {
684                                tap.setFacing(Misc.getAngleInDegrees(tap.getLocation(), closest.getLocation()) + 180f);
685                        }                       
686                }
687                public boolean isDone() {
688                        return false;
689                }
690                public boolean runWhilePaused() {
691                        return false;
692                }
693        }
694        
695        protected AddedEntity addCoronalTap(StarSystemAPI system) {
696                
697                if (DEBUG) System.out.println("Adding coronal tap to [" + system.getNameWithLowercaseType() + ", " + system.getLocation());
698                
699                String factionId = Factions.NEUTRAL;
700                
701                AddedEntity entity = null;
702                if (system.getType() == StarSystemType.TRINARY_2CLOSE) {
703                        EntityLocation loc = new EntityLocation();
704                        loc.location = new Vector2f();
705                        entity = addEntity(random, system, loc, Entities.CORONAL_TAP, factionId);
706                        if (entity != null) {
707                                system.addScript(new MakeCoronalTapFaceNearestStar(entity.entity));
708                        }
709                } else {
710                        WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>();
711                        WeightedRandomPicker<PlanetAPI> fallback = new WeightedRandomPicker<PlanetAPI>();
712                        for (PlanetAPI planet : system.getPlanets()) {
713                                if (!planet.isNormalStar()) continue;
714                                if (planet.getTypeId().equals(StarTypes.BLUE_GIANT) ||
715                                                planet.getTypeId().equals(StarTypes.BLUE_SUPERGIANT)) {
716                                        picker.add(planet);
717                                } else {
718                                        fallback.add(planet);
719                                }
720                        }
721                        if (picker.isEmpty()) {
722                                picker.addAll(fallback);
723                        }
724                        
725                        PlanetAPI star = picker.pick();
726                        if (star != null) {
727                                CustomEntitySpecAPI spec = Global.getSettings().getCustomEntitySpec(Entities.CORONAL_TAP);
728                                EntityLocation loc = new EntityLocation();
729                                float orbitRadius = star.getRadius() + spec.getDefaultRadius() + 100f;
730                                float orbitDays = orbitRadius / 20f;
731                                loc.orbit = Global.getFactory().createCircularOrbitPointingDown(star, random.nextFloat() * 360f, orbitRadius, orbitDays);
732                                entity = addEntity(random, system, loc, Entities.CORONAL_TAP, factionId);
733                        }
734                }
735                
736                
737                if (entity != null) {
738                        system.addScript(new CoronalTapParticleScript(entity.entity));
739//                      system.addCorona(entity.entity, Terrain.CORONA_JET,
740//                                      500f, // radius outside planet
741//                                      15f, // burn level of "wind"
742//                                      0f, // flare probability
743//                                      1f // CR loss mult while in it
744//                                      );
745                        
746//                      system.addTag(Tags.THEME_DERELICT);
747                        system.addTag(Tags.HAS_CORONAL_TAP);
748                }
749                
750                if (DEBUG) {
751                        if (entity != null) {
752                                System.out.println(String.format("  Added coronal tap to %s", system.getNameWithLowercaseType()));
753                        } else {
754                                System.out.println(String.format("  Failed to add coronal tap to %s", system.getNameWithLowercaseType()));
755                        }
756                }
757                return entity;
758        }
759        
760        
761        protected void addPKSystem(ThemeGenContext context) {
762                if (DEBUG) System.out.println("Looking for system to hide PK in");
763                
764                List<StarSystemAPI> preferred = new ArrayList<StarSystemAPI>();
765                List<StarSystemAPI> other = new ArrayList<StarSystemAPI>();
766                
767                for (Constellation c : context.constellations) {
768                        for (StarSystemAPI system : c.getSystems()) {
769                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
770                                
771                                if (system.isNebula()) continue;
772                                if (system.hasPulsar()) continue;
773                                if (system.hasBlackHole()) continue;
774                                
775                                boolean misc = system.hasTag(Tags.THEME_MISC_SKIP) || system.hasTag(Tags.THEME_MISC);
776                                if (system.hasTag(Tags.THEME_DERELICT)) misc = false;
777                                
778                                boolean nonLargeDerelict = system.hasTag(Tags.THEME_DERELICT) && 
779                                                                                !system.hasTag(Tags.THEME_DERELICT_MOTHERSHIP) &&
780                                                                                !system.hasTag(Tags.THEME_DERELICT_CRYOSLEEPER) &&
781                                                                                !system.hasTag(Tags.THEME_DERELICT_SURVEY_SHIP);
782                                
783                                boolean secondaryRuins = system.hasTag(Tags.THEME_RUINS_SECONDARY);
784                                boolean remnantNoFleets = system.hasTag(Tags.THEME_REMNANT_NO_FLEETS);
785                                boolean unsafe = system.hasTag(Tags.THEME_UNSAFE);
786                                
787                                if (unsafe || !(misc || nonLargeDerelict || secondaryRuins || remnantNoFleets)) {
788                                        continue;
789                                }
790                                
791                                int count = 0;
792                                for (PlanetAPI curr : system.getPlanets()) {
793                                        if (curr.isStar()) continue;
794                                        if (curr.isMoon()) continue;
795                                        if (curr.isGasGiant()) continue;
796                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;
797                                        if (curr.getCircularOrbitRadius() < 6000) continue;
798                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
799                                        count++;
800                                }
801                                
802                                if (count > 0) {
803                                        preferred.add(system);
804                                } else {
805                                        other.add(system);
806                                }
807                        }
808                }
809                
810                Comparator<StarSystemAPI> comp = new Comparator<StarSystemAPI>() {
811                        public int compare(StarSystemAPI o1, StarSystemAPI o2) {
812                                return (int) Math.signum(o2.getLocation().length() - o1.getLocation().length());
813                        }
814                };
815
816                List<StarSystemAPI> sorted = new ArrayList<StarSystemAPI>();
817                if (!preferred.isEmpty()) {
818                        sorted.addAll(preferred);
819                } else {
820                        sorted.addAll(other);
821                }
822                if (sorted.isEmpty()) {
823                        if (DEBUG) System.out.println("FAILED TO FIND SUITABLE SYSTEM FOR PK");
824                        return;
825                }
826                Collections.sort(sorted, comp);
827                
828                
829                // pick from some of the matching systems furthest from core
830                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(random);
831                for (int i = 0; i < 20 && i < sorted.size(); i++) {
832                        //sorted.get(i).addTag(Tags.PK_SYSTEM);
833                        picker.add(sorted.get(i), 1f);
834                }
835                
836                StarSystemAPI system = picker.pick();
837                
838                if (DEBUG) System.out.println("Adding PK to [" + system.getName() + "] at [" + system.getLocation() + "]");
839                setUpPKSystem(system);
840                
841                
842                if (DEBUG) System.out.println("Finished adding PK system\n\n\n\n\n");
843        }
844
845        protected void setUpPKSystem(StarSystemAPI system) {
846                system.addTag(Tags.THEME_SPECIAL);
847                system.addTag(Tags.PK_SYSTEM);
848                
849                Global.getSector().getPersistentData().put(PK_SYSTEM_KEY, system);
850                Global.getSector().getMemoryWithoutUpdate().set(PK_SYSTEM_KEY, system.getId());
851                
852                // - pick a planet at 6k range or higher to make into a tundra world
853                // - turn any planets with a lower hazard rating into barren and reassign their conditions 
854                
855                PlanetAPI tundra = null;
856                for (PlanetAPI curr : system.getPlanets()) {
857                        if (curr.isStar()) continue;
858                        //if (curr.isMoon()) continue;
859                        if (curr.isGasGiant()) continue;
860                        if (curr.getMarket() == null) continue;
861                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;
862                        if (curr.getCircularOrbitRadius() < 6000) continue;
863                        
864                        tundra = curr;
865                        break;
866                }
867                
868                //pick = null;
869                
870                // if there's no planet in a suitable range, create one
871                // could end up with a tundra world really far out if the system is full of stuff
872                // but has no planets, but it's unlikely
873                if (tundra == null) {
874                        List<OrbitGap> gaps = BaseThemeGenerator.findGaps(system.getCenter(), 6000, 20000, 800);
875                        float orbitRadius = 7000;
876                        if (!gaps.isEmpty()) {
877                                orbitRadius = (gaps.get(0).start + gaps.get(0).end) * 0.5f;
878                        }
879                        float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
880                        float radius = 100f + random.nextFloat() * 50f;
881                        float angle = random.nextFloat() * 360f;
882                        String type = Planets.BARREN;
883                        NamePick namePick = ProcgenUsedNames.pickName(NameGenData.TAG_PLANET, null, null);
884                        String name = namePick.nameWithRomanSuffixIfAny;
885                        tundra = system.addPlanet(Misc.genUID(), system.getStar(), name, type, angle, radius, orbitRadius, orbitDays);
886                        
887                        if (tundra == null) {
888                                if (DEBUG) System.out.println("FAILED TO CREATE PLANET IN PK SYSTEM");
889                                return;
890                        }
891                }
892                
893                tundra.setName("Sentinel");
894                tundra.getMarket().setName("Sentinel");
895                tundra.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
896                tundra.getMemoryWithoutUpdate().set(PK_PLANET_KEY, true);
897                Global.getSector().getPersistentData().put(PK_PLANET_KEY, tundra);
898                
899                if (DEBUG) System.out.println("Setting planet [" + tundra.getName() + "] to tundra");
900                tundra.changeType(Planets.TUNDRA, random);
901                tundra.getMarket().getConditions().clear();
902                PlanetConditionGenerator.generateConditionsForPlanet(null, tundra, system.getAge());
903                tundra.getMarket().removeCondition(Conditions.DECIVILIZED);
904                tundra.getMarket().removeCondition(Conditions.DECIVILIZED_SUBPOP);
905                tundra.getMarket().removeCondition(Conditions.RUINS_EXTENSIVE);
906                tundra.getMarket().removeCondition(Conditions.RUINS_SCATTERED);
907                tundra.getMarket().removeCondition(Conditions.RUINS_VAST);
908                tundra.getMarket().removeCondition(Conditions.RUINS_WIDESPREAD);
909                tundra.getMarket().removeCondition(Conditions.INIMICAL_BIOSPHERE);
910                
911                tundra.getMarket().removeCondition(Conditions.FARMLAND_POOR);
912                tundra.getMarket().removeCondition(Conditions.FARMLAND_ADEQUATE);
913                tundra.getMarket().removeCondition(Conditions.FARMLAND_RICH);
914                tundra.getMarket().removeCondition(Conditions.FARMLAND_BOUNTIFUL);
915                tundra.getMarket().addCondition(Conditions.FARMLAND_POOR);
916
917                
918                // make sure the tundra world is the best habitable world in-system so there's no questions
919                // as to why it was chosen by the survivors
920                float pickHazard = tundra.getMarket().getHazardValue();
921                
922                for (PlanetAPI curr : system.getPlanets()) {
923                        if (curr.isStar()) continue;
924                        if (curr.isGasGiant()) continue;
925                        if (curr.getMarket() == null) continue;
926                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;
927                        if (curr == tundra) continue;
928                        
929                        float h = curr.getMarket().getHazardValue();
930                        if (curr.hasCondition(Conditions.HABITABLE) && h <= pickHazard) {
931                                curr.changeType(Planets.BARREN_VENUSLIKE, random);
932                                curr.getMarket().getConditions().clear();
933                                PlanetConditionGenerator.generateConditionsForPlanet(null, curr, system.getAge());
934                        }
935                }
936                
937                for (SectorEntityToken curr : system.getEntitiesWithTag(Tags.STABLE_LOCATION)) {
938                        system.removeEntity(curr);
939                }
940                for (SectorEntityToken curr : system.getEntitiesWithTag(Tags.OBJECTIVE)) {
941                        system.removeEntity(curr);
942                }
943                
944                
945                List<OrbitGap> gaps = BaseThemeGenerator.findGaps(system.getCenter(), 2000, 20000, 800);
946                float orbitRadius = 7000;
947                if (!gaps.isEmpty()) {
948                        orbitRadius = (gaps.get(0).start + gaps.get(0).end) * 0.5f;
949                }
950                float radius = 500f + 200f * random.nextFloat();
951                float area = radius * radius * 3.14f;
952                int count = (int) (area / 80000f);
953                count *= 2;
954                if (count < 10) count = 10;
955                if (count > 100) count = 100;
956                float angle = random.nextFloat() * 360f;
957                float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
958                
959                SectorEntityToken field = system.addTerrain(Terrain.ASTEROID_FIELD,
960                                new AsteroidFieldParams(
961                                        radius, // min radius
962                                        radius + 100f, // max radius
963                                        count, // min asteroid count
964                                        count, // max asteroid count
965                                        4f, // min asteroid radius 
966                                        16f, // max asteroid radius
967                                        null)); // null for default name
968                
969                field.setCircularOrbit(system.getCenter(), angle, orbitRadius, orbitDays);
970                
971                SectorEntityToken cache = BaseThemeGenerator.addSalvageEntity(system, Entities.HIDDEN_CACHE, Factions.NEUTRAL);
972                cache.getMemoryWithoutUpdate().set(PK_CACHE_KEY, true);
973                cache.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
974                //cache.getLocation().set(10000, 10000);
975                cache.setCircularOrbit(field, 0, 0, 100f);
976                Misc.setDefenderOverride(cache, new DefenderDataOverride(Factions.HEGEMONY, 1f, 20, 20, 1));
977                
978                // Misc.addDefeatTrigger(fleet, trigger);
979                
980                // add a ship graveyard around the cache - Luddic Path ships, presumably from another
981                // Path operative that got farther along but never reported back
982                StarSystemData data = new StarSystemData();
983                WeightedRandomPicker<String> derelictShipFactions = new WeightedRandomPicker<String>(random);
984                derelictShipFactions.add(Factions.LUDDIC_PATH);
985                WeightedRandomPicker<String> hulls = new WeightedRandomPicker<String>(random);
986                hulls.add("prometheus2", 1f);
987                hulls.add("colossus2", 1f);
988                hulls.add("colossus2", 1f);
989                hulls.add("colossus2", 1f);
990                hulls.add("eradicator", 1f);
991                hulls.add("enforcer", 1f);
992                hulls.add("sunder", 1f);
993                hulls.add("venture_pather", 1f);
994                hulls.add("manticore_luddic_path", 1f);
995                hulls.add("cerberus_luddic_path", 1f);
996                hulls.add("hound_luddic_path", 1f);
997                hulls.add("buffalo2", 1f);
998                addShipGraveyard(data, field, derelictShipFactions, hulls);
999                for (AddedEntity ae : data.generated) {
1000                        SalvageSpecialAssigner.assignSpecials(ae.entity, true);
1001                }
1002                
1003                // add some remnant derelicts around a fringe jump-point
1004                // where the fight was
1005                float max = 0f;
1006                JumpPointAPI fringePoint = null;
1007                List<JumpPointAPI> points = system.getEntities(JumpPointAPI.class);
1008                for (JumpPointAPI curr : points) {
1009                        float dist = curr.getCircularOrbitRadius();
1010                        if (dist > max) {
1011                                max = dist;
1012                                fringePoint = curr;
1013                        }
1014                }
1015                
1016                if (fringePoint != null) {
1017                        data = new StarSystemData();
1018                        WeightedRandomPicker<String> remnantShipFactions = new WeightedRandomPicker<String>(random);
1019                        remnantShipFactions.add(Factions.REMNANTS);
1020                        hulls = new WeightedRandomPicker<String>(random);
1021                        hulls.add("radiant", 0.25f);
1022                        hulls.add("nova", 0.5f);
1023                        hulls.add("brilliant", 1f);
1024                        hulls.add("apex", 1f);
1025                        hulls.add("scintilla", 1f);
1026                        hulls.add("scintilla", 1f);
1027                        hulls.add("fulgent", 1f);
1028                        hulls.add("fulgent", 1f);
1029                        hulls.add("glimmer", 1f);
1030                        hulls.add("glimmer", 1f);
1031                        hulls.add("lumen", 1f);
1032                        hulls.add("lumen", 1f);
1033                        addShipGraveyard(data, fringePoint, remnantShipFactions, hulls);
1034                        addDebrisField(data, fringePoint, 400f);
1035                        
1036                        for (AddedEntity ae : data.generated) {
1037                                SalvageSpecialAssigner.assignSpecials(ae.entity, true);
1038                                if (ae.entity.getCustomPlugin() instanceof DerelictShipEntityPlugin) {
1039                                        DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) ae.entity.getCustomPlugin();
1040                                        plugin.getData().ship.condition = ShipCondition.WRECKED;
1041                                }
1042                        }
1043                }
1044                
1045                // Improvised dockyard where presumably the ship conversion took place
1046                SectorEntityToken dockyard = system.addCustomEntity("pk_dockyard",
1047                                "Sentinel Gantries", Entities.ORBITAL_DOCKYARD, "neutral");
1048
1049                dockyard.setCircularOrbitPointingDown(tundra, 45, 300, 30);             
1050                dockyard.setCustomDescriptionId("pk_orbital_dockyard");
1051                dockyard.getMemoryWithoutUpdate().set("$pkDockyard", true);
1052                
1053                //neutralStation.setInteractionImage("illustrations", "abandoned_station2");
1054                Misc.setAbandonedStationMarket("pk_dockyard", dockyard);
1055                
1056                
1057                // add some unused stuff to the dockyard
1058                CargoAPI cargo = dockyard.getMarket().getSubmarket(Submarkets.SUBMARKET_STORAGE).getCargo();
1059                cargo.initMothballedShips(Factions.HEGEMONY);
1060                
1061                CampaignFleetAPI temp = Global.getFactory().createEmptyFleet(Factions.HEGEMONY, null, true);
1062                temp.getFleetData().addFleetMember("enforcer_XIV_Elite");
1063                temp.getFleetData().addFleetMember("enforcer_XIV_Elite");
1064                temp.getFleetData().addFleetMember("eagle_xiv_Elite");
1065                temp.getFleetData().addFleetMember("dominator_XIV_Elite");
1066                DefaultFleetInflaterParams p = new DefaultFleetInflaterParams();
1067                p.quality = -1;
1068                temp.setInflater(new DefaultFleetInflater(p));
1069                temp.inflateIfNeeded();
1070                temp.setInflater(null);
1071
1072                int index = 0;
1073                for (FleetMemberAPI member : temp.getFleetData().getMembersListCopy()) {
1074                        for (String slotId : member.getVariant().getFittedWeaponSlots()) {
1075                                String weaponId = member.getVariant().getWeaponId(slotId);
1076                                if (random.nextFloat() < 0.5f) {
1077                                        member.getVariant().clearSlot(slotId);
1078                                }
1079                                if (random.nextFloat() < 0.25f) {
1080                                        cargo.addWeapons(weaponId, 1);
1081                                }
1082                        }
1083                        if (index == 0 || index == 2) {
1084                                cargo.getMothballedShips().addFleetMember(member);
1085                        }
1086                        index++;
1087                }
1088                cargo.addCommodity(Commodities.METALS, 50f + random.nextInt(51));
1089                
1090                List<CampaignFleetAPI> stations = getRemnantStations(true, false);
1091                float minDist = Float.MAX_VALUE;
1092                CampaignFleetAPI nexus = null;
1093                for (CampaignFleetAPI curr : stations) {
1094                        float dist = Misc.getDistanceLY(tundra, curr);
1095                        if (dist < minDist) {
1096                                minDist = dist;
1097                                nexus = curr;
1098                        }
1099                }
1100                if (nexus != null) {
1101                        if (DEBUG) System.out.println("Found Remnant nexus in [" + nexus.getContainingLocation().getName() + "]");
1102                        nexus.getMemoryWithoutUpdate().set(PK_NEXUS_KEY, true);
1103                        Global.getSector().getPersistentData().put(PK_NEXUS_KEY, nexus);
1104                        Global.getSector().getMemoryWithoutUpdate().set(PK_NEXUS_KEY, nexus.getContainingLocation().getId());
1105                        
1106                        Misc.addDefeatTrigger(nexus, "PKNexusDefeated");
1107                }
1108        }
1109        
1110        protected void addLOCRLuddicPlanet(ThemeGenContext context) {
1111                if (DEBUG) System.out.println("Looking for LOCR_LUDDIC planet");
1112                
1113                WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>(random);
1114                WeightedRandomPicker<PlanetAPI> picker_fallback = new WeightedRandomPicker<PlanetAPI>(random);
1115
1116                // looking for a habitable planet in the fringe WITH good farming
1117                for (Constellation c : context.constellations) {
1118                        for (StarSystemAPI system : c.getSystems()) {
1119                                
1120                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
1121                                if (system.isNebula()) continue;
1122                                if (system.hasPulsar()) continue;
1123                                if (system.hasBlackHole()) continue;
1124                                if (!system.hasTag(Tags.THEME_MISC_SKIP) && !system.hasTag(Tags.THEME_MISC)) continue;
1125                                if (system.hasTag(Tags.THEME_DERELICT)) continue;
1126                                
1127                                for (PlanetAPI curr : system.getPlanets()) {
1128                                        if (curr.isStar()) continue;
1129                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;  
1130                                        
1131                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1132                                        if (curr.getMarket().hasCondition(Conditions.WATER_SURFACE)) continue; // Ludd gets seasick, I guess.
1133                                        if (curr.isGasGiant()) continue; // I mean, let's just be sure, right?
1134                                        if (curr.getMarket().hasCondition(Conditions.DECIVILIZED_SUBPOP)) continue;
1135                                        
1136                                        // must have good farmland! Or at least OK farmland.
1137                                        if (curr.getMarket().hasCondition("farmland_rich") || curr.getMarket().hasCondition("farmland_bountiful"))
1138                                        {
1139                                                picker.add(curr);
1140                                        }
1141                                        else if ( curr.getMarket().hasCondition("farmland_adequate"))
1142                                        {
1143                                                picker_fallback.add(curr);
1144                                        }
1145                                }
1146                        }
1147                }
1148
1149                PlanetAPI planet = picker.pick();
1150                if (planet == null) 
1151                {
1152                        planet = picker_fallback.pick();
1153                }
1154                
1155                if (planet != null) {
1156                        
1157                        // add the ship they came in on
1158                        
1159                        DerelictShipData params = new DerelictShipData(new PerShipData("nebula_Standard", ShipCondition.BATTERED, 0f), false);
1160                        params.ship.shipName = "CGR Light of Exultation";
1161                        params.ship.nameAlwaysKnown = true;
1162                        //params.ship.fleetMemberId = id;
1163                        
1164                        SectorEntityToken ship = BaseThemeGenerator.addSalvageEntity(planet.getStarSystem(), Entities.WRECK, Factions.NEUTRAL, params);
1165                        ship.setDiscoverable(true);
1166                        float orbitDays = 200f / (10f + (float) Math.random() * 5f);
1167                        ship.setCircularOrbit(planet, (float) Math.random() * 360f, planet.getRadius() + 100f, orbitDays);
1168                        ShipRecoverySpecialCreator creator = new ShipRecoverySpecialCreator(null, 0, 0, false, null, null);
1169                        Misc.setSalvageSpecial(ship, creator.createSpecial(ship, null));
1170                        
1171                        Global.getSector().getMemoryWithoutUpdate().set(LOCR_LUDDIC_TRANSPORT_KEY, ship);
1172                        //ship.getMemoryWithoutUpdate().set(LOCR_LUDDIC, true);
1173                        
1174                        
1175                        if (DEBUG) System.out.println("Adding LOCR_LUDDIC flag to [" + planet.getName() + "] in [" + planet.getContainingLocation().getNameWithLowercaseType() + "]");
1176                        Global.getSector().getMemoryWithoutUpdate().set(LOCR_LUDDIC_PLANET_KEY, planet);
1177                        planet.getMemoryWithoutUpdate().set(LOCR_LUDDIC, true);
1178                        planet.getMemoryWithoutUpdate().set(LOCR_BLOCK_FIRST_SURVEY, true);
1179                        
1180                        long seed = StarSystemGenerator.random.nextLong();
1181                        planet.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
1182                        
1183                } else {
1184                        if (DEBUG) System.out.println("Failed to find a LOCR_LUDDIC planet, may Ludd forgive you.");
1185                }
1186                if (DEBUG) System.out.println("Finished adding LOCR_LUDDIC  planet\n\n\n");
1187        }
1188        
1189/* Maybe later. -dgb
1190        protected void addLOCRUtopiaPlanet(ThemeGenContext context) {
1191                if (DEBUG) System.out.println("Looking for LOCR_UTOPIA planet");
1192                
1193                WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>(random);
1194
1195                // looking for a habitable planet in the fringe
1196                for (Constellation c : context.constellations) {
1197                        for (StarSystemAPI system : c.getSystems()) {
1198                                
1199                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
1200                                if (system.isNebula()) continue;
1201                                if (system.hasPulsar()) continue;
1202                                if (system.hasBlackHole()) continue;
1203                                if (!system.hasTag(Tags.THEME_MISC_SKIP) && !system.hasTag(Tags.THEME_MISC)) continue;
1204                                if (system.hasTag(Tags.THEME_DERELICT)) continue;
1205                                
1206                                for (PlanetAPI curr : system.getPlanets()) {
1207                                        if (curr.isStar()) continue;
1208                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;  
1209                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1210                                        if (curr.getMarket().hasCondition(Conditions.DECIVILIZED_SUBPOP)) continue; // no competition, please.
1211                                        if (curr.getMarket().hasCondition(Conditions.WATER_SURFACE)) continue; // I'd have to write around this.
1212                                        if (!curr.getMarket().hasCondition(Conditions.HABITABLE)) continue;
1213                                        
1214                                        picker.add(curr);
1215                                }
1216                        }
1217                }
1218                
1219                PlanetAPI planet =  picker.pick();
1220
1221                if (planet != null) {
1222                        if (DEBUG) System.out.println("Adding LOCR_UTOPIA flag to [" + planet.getName() + "] in [" + planet.getContainingLocation().getNameWithLowercaseType() + "]");
1223                        Global.getSector().getMemoryWithoutUpdate().set(LOCR_UTOPIA_PLANET_KEY, planet);
1224                        planet.getMemoryWithoutUpdate().set(LOCR_UTOPIA, true);
1225                        planet.getMemoryWithoutUpdate().set(LOCR_BLOCK_FIRST_SURVEY, true);
1226                        
1227                        long seed = StarSystemGenerator.random.nextLong();
1228                        planet.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
1229                        
1230                } else {
1231                        if (DEBUG) System.out.println("Failed to find a planet for LOCR_UTOPIA; the dream is dead :( ");
1232                }
1233                if (DEBUG) System.out.println("Finished adding LOCR_UTOPIA planet\n\n\n");
1234        }
1235*/
1236        
1237        protected void addLOCRMinersPlanet(ThemeGenContext context) {
1238                if (DEBUG) System.out.println("Looking for LOCR_MINERS planet");
1239                
1240                WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>(random);
1241
1242                // looking for a non-habitable planet or moon with good mining in the fringe
1243                for (Constellation c : context.constellations) {
1244                        for (StarSystemAPI system : c.getSystems()) {
1245                                
1246                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
1247                                if (system.hasPulsar()) continue;
1248                                if (!system.hasTag(Tags.THEME_MISC_SKIP) && !system.hasTag(Tags.THEME_MISC)) continue;
1249                                if (system.hasTag(Tags.THEME_DERELICT)) continue;
1250                                
1251                                for (PlanetAPI curr : system.getPlanets()) {
1252                                        if (curr.isStar()) continue;
1253                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;  
1254                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1255                                        if (curr.getMarket().hasCondition(Conditions.HABITABLE)) continue;
1256                                        if (curr.getMarket().hasCondition(Conditions.EXTREME_TECTONIC_ACTIVITY)) continue; // It has to be a GOOD planet.
1257                                        if (curr.getMarket().hasCondition(Conditions.DECIVILIZED_SUBPOP)) continue;
1258                                        if (curr.getMarket().hasCondition(Conditions.WATER_SURFACE)) continue; // don't want to write around this.
1259                                        if (curr.isGasGiant()) continue;
1260                                        if (!( curr.getMarket().hasCondition(Conditions.VOLATILES_PLENTIFUL) || 
1261                                                        curr.getMarket().hasCondition(Conditions.ORGANICS_PLENTIFUL) ||
1262                                                        curr.getMarket().hasCondition(Conditions.RARE_ORE_ULTRARICH) ) ) continue;      
1263                                        //              curr.getMarket().hasCondition(Conditions.ORE_ULTRARICH) ) ) continue;   
1264                                        
1265                                        picker.add(curr);
1266                                }
1267                        }
1268                }
1269                
1270                PlanetAPI planet =  picker.pick();
1271
1272                if (planet != null) {
1273                        if (DEBUG) System.out.println("Adding LOCR_MINERS flag to [" + planet.getName() + "] in [" + planet.getContainingLocation().getNameWithLowercaseType() + "]");
1274                        Global.getSector().getMemoryWithoutUpdate().set(LOCR_MINERS_PLANET_KEY, planet);
1275                        planet.getMemoryWithoutUpdate().set(LOCR_MINERS, true);
1276                        planet.getMemoryWithoutUpdate().set(LOCR_BLOCK_FIRST_SURVEY, true);
1277                        
1278                        long seed = StarSystemGenerator.random.nextLong();
1279                        planet.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
1280                        
1281                } else {
1282                        if (DEBUG) System.out.println("Failed to find a planet for LOCR_MINERS.");
1283                }
1284                if (DEBUG) System.out.println("Finished adding LOCR_MINERS planet\n\n\n\n\n");
1285        }
1286        
1287        protected void addLOCRPiratePlanet(ThemeGenContext context) {
1288                if (DEBUG) System.out.println("Looking for LOCR_PIRATE planet");
1289                
1290                WeightedRandomPicker<PlanetAPI> picker = new WeightedRandomPicker<PlanetAPI>(random);
1291                
1292                // looking for an obscure non-habitable planet or moon
1293                for (Constellation c : context.constellations) {
1294                        for (StarSystemAPI system : c.getSystems()) {
1295                                
1296                                if (system.hasTag(Tags.THEME_SPECIAL)) continue;
1297                                if (system.hasPulsar()) continue;
1298                                if (!system.hasTag(Tags.THEME_MISC_SKIP) && !system.hasTag(Tags.THEME_MISC)) continue;
1299                                if (system.hasTag(Tags.THEME_DERELICT)) continue;
1300                                
1301                                for (PlanetAPI curr : system.getPlanets()) {
1302                                        if (curr.isStar()) continue;
1303                                        if (!curr.getMarket().isPlanetConditionMarketOnly()) continue;  
1304                                        if (curr.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1305                                        if (curr.getMarket().hasCondition(Conditions.DECIVILIZED_SUBPOP)) continue; // no competition, please.
1306                                        if (curr.getMarket().hasCondition(Conditions.HABITABLE)) continue; // should be a POS
1307                                        if (curr.isGasGiant()) continue;
1308                                        
1309                                        picker.add(curr);
1310                                }
1311                        }
1312                }
1313
1314                PlanetAPI planet =  picker.pick();
1315
1316                if (planet != null) {
1317                        if (DEBUG) System.out.println("Adding LOCR_PIRATE flag to [" + planet.getName() + "] in [" + planet.getContainingLocation().getNameWithLowercaseType() + "]");
1318                        Global.getSector().getMemoryWithoutUpdate().set(LOCR_PIRATE_PLANET_KEY, planet);
1319                        planet.getMemoryWithoutUpdate().set(LOCR_PIRATE, true);
1320                        planet.getMemoryWithoutUpdate().set(LOCR_BLOCK_FIRST_SURVEY, true);
1321                        
1322                        long seed = StarSystemGenerator.random.nextLong();
1323                        planet.addTag(Tags.NOT_RANDOM_MISSION_TARGET);
1324                        
1325                } else {
1326                        if (DEBUG) System.out.println("Failed to find a planet for LOCR_PIRATE; the dream is dead :( ");
1327                }
1328                if (DEBUG) System.out.println("Finished adding LOCR_PIRATE planet\n\n\n");
1329        }
1330        
1331        
1332        public static List<CampaignFleetAPI> getRemnantStations(boolean includeDamaged, boolean onlyDamaged) {
1333                List<CampaignFleetAPI> stations = new ArrayList<CampaignFleetAPI>();
1334                for (StarSystemAPI system : Global.getSector().getStarSystems()) {
1335                        if (!system.hasTag(Tags.THEME_REMNANT_MAIN)) continue;
1336                        if (system.hasTag(Tags.THEME_REMNANT_DESTROYED)) continue;
1337                        
1338                        for (CampaignFleetAPI fleet : system.getFleets()) {
1339                                if (!fleet.isStationMode()) continue;
1340                                if (!Factions.REMNANTS.equals(fleet.getFaction().getId())) continue;
1341                                
1342                                boolean damaged = fleet.getMemoryWithoutUpdate().getBoolean("$damagedStation");
1343                                if (damaged && !includeDamaged) continue;
1344                                if (!damaged && onlyDamaged) continue;
1345                                
1346                                stations.add(fleet);
1347                        }
1348                }
1349                return stations;
1350        }
1351}
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367