001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.EnumSet;
007import java.util.HashSet;
008import java.util.LinkedHashMap;
009import java.util.LinkedHashSet;
010import java.util.List;
011import java.util.Random;
012import java.util.Set;
013
014import org.lwjgl.util.vector.Vector2f;
015
016import com.fs.starfarer.api.Global;
017import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
018import com.fs.starfarer.api.campaign.CampaignTerrainPlugin;
019import com.fs.starfarer.api.campaign.CargoAPI;
020import com.fs.starfarer.api.campaign.CircularOrbitWithSpinAPI;
021import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
022import com.fs.starfarer.api.campaign.LocationAPI;
023import com.fs.starfarer.api.campaign.OrbitAPI;
024import com.fs.starfarer.api.campaign.PlanetAPI;
025import com.fs.starfarer.api.campaign.SectorEntityToken;
026import com.fs.starfarer.api.campaign.StarSystemAPI;
027import com.fs.starfarer.api.campaign.econ.MarketAPI;
028import com.fs.starfarer.api.campaign.rules.MemoryAPI;
029import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin;
030import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData;
031import com.fs.starfarer.api.impl.campaign.ids.Conditions;
032import com.fs.starfarer.api.impl.campaign.ids.Entities;
033import com.fs.starfarer.api.impl.campaign.ids.Factions;
034import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
035import com.fs.starfarer.api.impl.campaign.ids.Tags;
036import com.fs.starfarer.api.impl.campaign.procgen.Constellation;
037import com.fs.starfarer.api.impl.campaign.procgen.ObjectiveGenDataSpec;
038import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec;
039import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData;
040import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
041import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator.LagrangePointType;
042import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator.StarSystemType;
043import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageEntity;
044import com.fs.starfarer.api.impl.campaign.terrain.AsteroidBeltTerrainPlugin;
045import com.fs.starfarer.api.impl.campaign.terrain.AsteroidFieldTerrainPlugin;
046import com.fs.starfarer.api.impl.campaign.terrain.BaseRingTerrain;
047import com.fs.starfarer.api.impl.campaign.terrain.BaseTiledTerrain;
048import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin;
049import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldParams;
050import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldSource;
051import com.fs.starfarer.api.impl.campaign.terrain.MagneticFieldTerrainPlugin;
052import com.fs.starfarer.api.impl.campaign.terrain.NebulaTerrainPlugin;
053import com.fs.starfarer.api.impl.campaign.terrain.PulsarBeamTerrainPlugin;
054import com.fs.starfarer.api.impl.campaign.terrain.RadioChatterTerrainPlugin;
055import com.fs.starfarer.api.impl.campaign.terrain.RingSystemTerrainPlugin;
056import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin;
057import com.fs.starfarer.api.util.Misc;
058import com.fs.starfarer.api.util.WeightedRandomPicker;
059
060public abstract class BaseThemeGenerator implements ThemeGenerator {
061        
062        public static enum HabitationLevel {
063                LOW,
064                MEDIUM,
065                HIGH,
066        }
067        
068        public static class StarSystemData {
069                public StarSystemAPI system;
070                public List<PlanetAPI> stars = new ArrayList<PlanetAPI>();
071                public List<PlanetAPI> planets = new ArrayList<PlanetAPI>();
072                public List<PlanetAPI> habitable = new ArrayList<PlanetAPI>();
073                public List<PlanetAPI> gasGiants = new ArrayList<PlanetAPI>();
074                public List<PlanetAPI> resourceRich = new ArrayList<PlanetAPI>();
075                
076                public Set<SectorEntityToken> alreadyUsed = new LinkedHashSet<SectorEntityToken>();
077                
078                public Set<AddedEntity> generated = new LinkedHashSet<AddedEntity>();
079                
080                public boolean isBlackHole() {
081                        return system.getStar() != null && system.getStar().getSpec().isBlackHole();
082                }
083                
084                public boolean isPulsar() {
085                        //return system.getStar() != null && system.getStar().getSpec().getPlanetType().equals(StarTypes.NEUTRON_STAR);
086                        return system.hasPulsar();
087                }
088                
089                public boolean isNebula() {
090                        return system.isNebula();
091                }
092
093                @Override
094                public String toString() {
095                        return String.format(system.getName() + " %d %d %d %d %d", stars.size(), planets.size(), habitable.size(), 
096                                                                 gasGiants.size(),
097                                                                 resourceRich.size());
098                }
099                
100                
101        }
102        
103        
104        public static boolean DEBUG = Global.getSettings().isDevMode();
105        
106        
107        public static class AddedEntity {
108                public SectorEntityToken entity;
109                public EntityLocation location;
110                public String entityType;
111                public AddedEntity(SectorEntityToken entity, EntityLocation location, String entityType) {
112                        this.entity = entity;
113                        this.location = location;
114                        this.entityType = entityType;
115                }
116        }
117        
118        
119        public static enum LocationType {
120                PLANET_ORBIT,
121                GAS_GIANT_ORBIT,
122                JUMP_ORBIT,
123                NEAR_STAR,
124                IN_ASTEROID_BELT,
125                IN_ASTEROID_FIELD,
126                IN_RING,
127                
128                L_POINT,
129                IN_SMALL_NEBULA,
130                
131                OUTER_SYSTEM,
132                STAR_ORBIT,
133        }
134
135        public static class EntityLocation {
136                public LocationType type;
137                public Vector2f location = null;
138                public OrbitAPI orbit = null;
139                
140                
141                @Override
142                public String toString() {
143                        return String.format("Type: %s, orbitPeriod: %s", type.name(), orbit == null ? "null" : "" + orbit.getOrbitalPeriod());
144                }
145                
146                
147        }
148        
149        abstract public int getOrder();
150        abstract public String getThemeId();
151
152        public float getWeight() {
153                return 100f;
154        }
155        
156        protected Random random;
157        public BaseThemeGenerator() {
158                random = StarSystemGenerator.random;
159        }
160        
161
162        abstract public void generateForSector(ThemeGenContext context, float allowedSectorFraction);
163        
164        
165        public void addShipGraveyard(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> factions) {
166                if (random.nextFloat() >= chanceToAddAny) return;
167                int num = min + random.nextInt(max - min + 1);
168                
169                for (int i = 0; i < num; i++) {
170                        LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
171//                      weights.put(LocationType.IN_ASTEROID_BELT, 5f);
172//                      weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
173//                      weights.put(LocationType.IN_RING, 5f);
174//                      weights.put(LocationType.IN_SMALL_NEBULA, 5f);
175//                      weights.put(LocationType.L_POINT, 5f);
176//                      weights.put(LocationType.NEAR_STAR, 5f);
177//                      weights.put(LocationType.OUTER_SYSTEM, 5f);
178                        weights.put(LocationType.STAR_ORBIT, 5f);
179                        WeightedRandomPicker<EntityLocation> locs = getLocations(random, data.system, null, 1000f, weights);
180                        EntityLocation loc = locs.pick();
181                        
182                        if (loc != null) {
183                                SectorEntityToken token = data.system.createToken(0, 0);
184                                data.system.addEntity(token);
185                                setEntityLocation(token, loc, null);
186                                addShipGraveyard(data, token, factions);
187                        }
188                }
189                
190        }
191        
192        public void addShipGraveyard(StarSystemData data, SectorEntityToken focus, WeightedRandomPicker<String> factions) {
193                addShipGraveyard(data, focus, factions, null);
194        }
195        public void addShipGraveyard(StarSystemData data, SectorEntityToken focus, WeightedRandomPicker<String> factions, 
196                        WeightedRandomPicker<String> hulls) {
197                int numShips = random.nextInt(9) + 3;
198                //numShips = 12;
199                if (DEBUG) System.out.println("    Adding ship graveyard (" + numShips + " ships)");
200                
201                WeightedRandomPicker<Float> bands = new WeightedRandomPicker<Float>(random);
202                for (int i = 0; i < numShips + 5; i++) {
203                        bands.add(new Float(140 + i * 20), (i + 1) * (i + 1));
204                }
205                
206//              WeightedRandomPicker<String> factions = new WeightedRandomPicker<String>(random);
207//              factions.add(Factions.TRITACHYON, 10f);
208//              factions.add(Factions.HEGEMONY, 7f);
209//              factions.add(Factions.INDEPENDENT, 3f);
210                
211                for (int i = 0; i < numShips; i++) {
212                        float radius = bands.pickAndRemove();
213                        
214                        DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, random, DerelictShipEntityPlugin.getDefaultSModProb());
215                        if (hulls != null && !hulls.isEmpty()) {
216                                params = DerelictShipEntityPlugin.createHull(hulls.pickAndRemove(), random, DerelictShipEntityPlugin.getDefaultSModProb());
217                        }
218                        if (params != null) {
219                                CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) addSalvageEntity(random,
220                                                                        focus.getContainingLocation(),
221                                                                        Entities.WRECK, Factions.NEUTRAL, params);
222                                entity.setDiscoverable(true);
223                                float orbitDays = radius / (5f + random.nextFloat() * 10f);
224                                entity.setCircularOrbit(focus, random.nextFloat() * 360f, radius, orbitDays);
225                                if (DEBUG) System.out.println("      Added ship: " + 
226                                                ((DerelictShipEntityPlugin)entity.getCustomPlugin()).getData().ship.variantId);
227                                
228                                AddedEntity added = new AddedEntity(entity, null, Entities.WRECK);
229                                data.generated.add(added);
230                        }
231                }
232        }
233        
234        public void addDerelictShips(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> factions) {
235                if (random.nextFloat() >= chanceToAddAny) return;
236                
237                //data.system.updateAllOrbits();
238                
239                int num = min + random.nextInt(max - min + 1);
240                for (int i = 0; i < num; i++) {
241                        EntityLocation loc = pickAnyLocation(random, data.system, 70f, null);
242                        addDerelictShip(data, loc, factions);
243                }
244                
245        }
246        
247        public void addMiningStations(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> stationTypes) {
248                if (random.nextFloat() >= chanceToAddAny) return;
249                
250                int num = min + random.nextInt(max - min + 1);
251                if (DEBUG) System.out.println("    Adding " + num + " mining stations");
252                for (int i = 0; i < num; i++) {
253                        List<PlanetAPI> miningCandidates = new ArrayList<PlanetAPI>();
254                        miningCandidates.addAll(data.gasGiants);
255                        miningCandidates.addAll(data.resourceRich);
256                        
257                        LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
258                        weights.put(LocationType.IN_ASTEROID_BELT, 10f);
259                        weights.put(LocationType.IN_ASTEROID_FIELD, 10f);
260                        weights.put(LocationType.IN_RING, 10f);
261                        weights.put(LocationType.IN_SMALL_NEBULA, 10f);
262                        WeightedRandomPicker<EntityLocation> locs = getLocations(random, data.system, null, 100f, weights);
263                        EntityLocation loc = locs.pick();
264                        
265                        String type = stationTypes.pick();
266                        if (loc != null || !miningCandidates.isEmpty()) {
267                                if ((random.nextFloat() > 0.5f && loc != null) || miningCandidates.isEmpty()) {
268                                        addStation(loc, data, type, Factions.NEUTRAL);
269                                } else {
270                                        PlanetAPI planet = miningCandidates.get(random.nextInt(miningCandidates.size()));
271                                        EntityLocation planetOrbitLoc = createLocationAtRandomGap(random, planet, 100f);
272                                        addStation(planetOrbitLoc, data, type, Factions.NEUTRAL);
273                                        data.alreadyUsed.add(planet);
274                                }
275                        }
276                }
277        }
278        
279        
280        public static float NOT_HABITABLE_PLANET_PROB = 0.1f;
281        public static float ORBITAL_HABITAT_PROB = 0.5f;
282        
283        public void addHabCenters(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> stationTypes) {
284                if (random.nextFloat() >= chanceToAddAny) return;
285                
286                WeightedRandomPicker<PlanetAPI> habPlanets = new WeightedRandomPicker<PlanetAPI>(random);
287                for (PlanetAPI planet : data.habitable) {
288                        float h = planet.getMarket().getHazardValue();
289                        h -= 0.5f;
290                        if (h < 0.1f) h = 0.1f;
291                        float w = 1f / h;
292                        habPlanets.add(planet, w);
293                }
294                
295                WeightedRandomPicker<PlanetAPI> otherPlanets = new WeightedRandomPicker<PlanetAPI>(random);
296                for (PlanetAPI planet : data.planets) {
297                        if (data.habitable.contains(planet)) continue;
298                        otherPlanets.add(planet);
299                }
300                
301                int num = min + random.nextInt(max - min + 1);
302                if (DEBUG) System.out.println("    Adding up to " + num + " hab centers on planets/in orbit");
303                for (int i = 0; i < num; i++) {
304                        int option = 0;
305                        if (!habPlanets.isEmpty() && (random.nextFloat() > NOT_HABITABLE_PLANET_PROB || i == 0)) {
306                                option = 0; // habitable planet
307                        } else {
308                                if (otherPlanets.isEmpty() || random.nextFloat() < ORBITAL_HABITAT_PROB) {
309                                        option = 2; // orbital habitat
310                                } else {
311                                        option = 1; // other planet
312                                }
313                        }
314                        
315                        if (option == 0) {
316                                PlanetAPI planet = habPlanets.pickAndRemove();
317                                addRuins(planet);
318                                data.alreadyUsed.add(planet);
319                        } else if (option == 1) {
320                                PlanetAPI planet = otherPlanets.pickAndRemove();
321                                addRuins(planet);
322                                data.alreadyUsed.add(planet);
323                        } else if (option == 2) {
324                                String type = stationTypes.pick();
325                                EntityLocation loc = pickCommonLocation(random, data.system, 100f, true, null);
326                                addStation(loc, data, type, Factions.NEUTRAL);
327                        }
328                }
329        }
330        
331        
332        public void addResearchStations(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> stationTypes) {
333                if (random.nextFloat() >= chanceToAddAny) return;
334                
335                int num = min + random.nextInt(max - min + 1);
336                if (DEBUG) System.out.println("    Adding " + num + " research stations");
337                for (int i = 0; i < num; i++) {
338                        String type = stationTypes.pick();
339                        
340                        List<PlanetAPI> researchCandidates = new ArrayList<PlanetAPI>();
341                        researchCandidates.addAll(data.gasGiants);
342                        
343                        LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
344                        weights.put(LocationType.IN_SMALL_NEBULA, 5f);
345                        weights.put(LocationType.GAS_GIANT_ORBIT, 10f);
346                        weights.put(LocationType.NEAR_STAR, 5f);
347                        WeightedRandomPicker<EntityLocation> locs = getLocations(random, data.system, data.alreadyUsed, 100f, weights);
348                        EntityLocation loc = locs.pick();
349                        
350                        if (loc != null) {
351                                AddedEntity added = addStation(loc, data, type, Factions.NEUTRAL);
352                                if (loc.orbit != null && loc.orbit.getFocus() instanceof PlanetAPI) {
353                                        PlanetAPI planet = (PlanetAPI) loc.orbit.getFocus();
354                                        if (!planet.isStar()) {
355                                                data.alreadyUsed.add(planet);
356                                        }
357                                }
358                        }
359                }
360        }
361        
362        
363        public void addRuins(PlanetAPI planet) {
364                if (planet == null) return;
365                
366                MarketAPI market = planet.getMarket();
367                clearRuins(market);
368                
369                String ruins = pickRuinsType(planet);
370                if (DEBUG) System.out.println("      Added " + ruins + " to " + market.getName());
371                market.addCondition(ruins);
372                if (shouldHaveDecivilized(planet, ruins)) {
373                        if (DEBUG) System.out.println("        Added decivilized to " + market.getName());
374                        market.addCondition(Conditions.DECIVILIZED);
375                }
376        }
377        
378        public boolean shouldHaveDecivilized(PlanetAPI planet, String ruins) {
379                float chance = 0.25f;
380                
381                if (planet.getMarket().hasCondition(Conditions.HABITABLE)) {
382                        chance += 0.25f;
383                }
384                
385                if (ruins != null && ruins.equals(Conditions.RUINS_EXTENSIVE)) {
386                        chance += 0.1f;
387                }
388                if (ruins != null && ruins.equals(Conditions.RUINS_VAST)) {
389                        chance += 0.2f;
390                }
391                
392                return random.nextFloat() < chance;
393        }
394        
395
396        public List<AddedEntity> addObjectives(StarSystemData data, float prob) {
397                List<AddedEntity> result = new ArrayList<AddedEntity>();
398
399                Set<String> used = new HashSet<String>();
400                
401                float mult = 2f;
402                for (SectorEntityToken loc : data.system.getEntitiesWithTag(Tags.STABLE_LOCATION)) {
403                        mult *= 0.5f;
404                        if (random.nextFloat() >= prob * mult) continue;
405                        
406                        WeightedRandomPicker<ObjectiveGenDataSpec> picker = new WeightedRandomPicker<ObjectiveGenDataSpec>(random);
407                        for (Object o : Global.getSettings().getAllSpecs(ObjectiveGenDataSpec.class)) {
408                                ObjectiveGenDataSpec spec = (ObjectiveGenDataSpec) o;
409                                if (used.contains(spec.getCategory())) continue;
410                                picker.add(spec, spec.getFrequency());
411                        }
412                        
413                        ObjectiveGenDataSpec pick = picker.pick();
414                        if (pick == null) break;
415                        
416                        used.add(pick.getCategory());
417                        
418                        SectorEntityToken built = data.system.addCustomEntity(null,
419                                                                                                                                  null,
420                                                                                                                                  pick.getId(), // type of object, defined in custom_entities.json
421                                                                                                                                  Factions.NEUTRAL); // faction
422                        built.getMemoryWithoutUpdate().set(MemFlags.OBJECTIVE_NON_FUNCTIONAL, true);
423                        if (loc.getOrbit() != null) {
424                                built.setOrbit(loc.getOrbit().makeCopy());
425                        }
426                        built.setLocation(loc.getLocation().x, loc.getLocation().y);
427                        data.system.removeEntity(loc);
428                        
429                        AddedEntity e = new AddedEntity(built, null, pick.getId());
430                        result.add(e);
431                }
432                
433                
434                return result;
435        }
436        
437        public static ObjectiveGenDataSpec getObjectiveSpec(String id) {
438                ObjectiveGenDataSpec spec = (ObjectiveGenDataSpec) Global.getSettings().getSpec(ObjectiveGenDataSpec.class, id, false);
439                return spec;
440        }
441        
442        public AddedEntity addCommRelay(StarSystemData data, float prob) {
443                if (random.nextFloat() >= prob) return null;
444                
445                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
446                weights.put(LocationType.STAR_ORBIT, 10f);
447                weights.put(LocationType.OUTER_SYSTEM, 10f);
448                WeightedRandomPicker<EntityLocation> locs = getLocations(random, data.system, null, 100f, weights);
449                EntityLocation loc = locs.pick();
450                
451                AddedEntity added = addNonSalvageEntity(data.system, loc, Entities.COMM_RELAY, Factions.NEUTRAL);
452                if (DEBUG && added != null) System.out.println("    Added comm relay");
453                
454                if (added != null) {
455                        convertOrbitNoSpin(added.entity);
456                        added.entity.getMemoryWithoutUpdate().set(MemFlags.OBJECTIVE_NON_FUNCTIONAL, true);
457                }
458                
459                return added;
460        }
461        
462        //public static boolean USE_NEW_SPAWN = false;
463        public AddedEntity addInactiveGate(StarSystemData data, float prob, float probDebris, float probShips, WeightedRandomPicker<String> factions) {
464                if (random.nextFloat() >= prob) return null;
465                
466//              USE_NEW_SPAWN = false;
467//              if (data.system != null && Misc.getDistance(data.system.getLocation(), new Vector2f(62500, -5500)) < 1000) {
468//                      System.out.println("efwefwefe");
469//                      USE_NEW_SPAWN = true;
470//              }
471                
472                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
473                weights.put(LocationType.STAR_ORBIT, 10f);
474                weights.put(LocationType.OUTER_SYSTEM, 10f);
475                WeightedRandomPicker<EntityLocation> locs = getLocations(random, data.system, null, 100f, weights);
476                EntityLocation loc = locs.pick();
477                
478                AddedEntity added = addNonSalvageEntity(data.system, loc, Entities.INACTIVE_GATE, Factions.NEUTRAL);
479                if (DEBUG && added != null) System.out.println("    Added inactive gate to " + data.system.getNameWithLowercaseTypeShort());
480                
481                if (added != null) {
482                        convertOrbitNoSpin(added.entity);
483                        
484                        if (random.nextFloat() < probDebris) {
485                                if (DEBUG && added != null) System.out.println("      Added debris field around gate");
486                                addDebrisField(data, added.entity, 500f + random.nextFloat() * 100f);
487                                if (random.nextFloat() < probShips) {
488                                        if (DEBUG && added != null) System.out.println("      Added ship graveyard around gate");
489                                        addShipGraveyard(data, added.entity, factions);
490                                }
491                        }
492                }
493                
494                return added;
495        }
496        
497        
498        public String pickRuinsType(PlanetAPI planet) {
499                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
500                
501                float hazard = planet.getMarket().getHazardValue();
502
503                float add1 = 0, add2 = 0;
504                
505                if (hazard <= 1f) {
506                        add1 = 10f;
507                        add2 = 5f;
508                } else if (hazard <= 1.25f) {
509                        add1 = 5f;
510                        add2 = 1f;
511                }
512                
513                picker.add(Conditions.RUINS_SCATTERED, 10f);
514                picker.add(Conditions.RUINS_WIDESPREAD, 10f);
515                picker.add(Conditions.RUINS_EXTENSIVE, 3f + add1);
516                picker.add(Conditions.RUINS_VAST, 1f + add2);
517                
518                return picker.pick();
519        }
520        
521        
522        public AddedEntity addStation(EntityLocation loc, StarSystemData data, String customEntityId, String factionId) {
523                if (loc == null) return null;
524                
525                AddedEntity station = addEntity(random, data.system, loc, customEntityId, factionId);
526                if (station != null) {
527                        data.generated.add(station);
528                }
529                SectorEntityToken focus = station.entity.getOrbitFocus();
530                if (DEBUG) System.out.println("      Added " + customEntityId);
531                if (focus instanceof PlanetAPI) {
532                        PlanetAPI planet = (PlanetAPI) focus;
533                        data.alreadyUsed.add(planet);
534                        
535                        boolean nearStar = planet.isStar() && station.entity.getOrbit() != null && station.entity.getCircularOrbitRadius() < 5000; 
536                        
537                        if (planet.isStar() && !nearStar) {
538//                              station.entity.setFacing(random.nextFloat() * 360f);
539//                              convertOrbitNoSpin(station.entity);
540                        } else {
541                                convertOrbitPointingDown(station.entity);
542                        }
543                }
544                
545//              station.entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_DEFENDER_FACTION, Factions.REMNANTS);
546//              station.entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_DEFENDER_PROB, 1f);
547                
548                return station;
549        }
550        
551        
552        public void addCaches(StarSystemData data, float chanceToAddAny, int min, int max, WeightedRandomPicker<String> cacheTypes) {
553                if (random.nextFloat() >= chanceToAddAny) return;
554                
555                int num = min + random.nextInt(max - min + 1);
556                if (DEBUG) System.out.println("    Adding " + num + " resource caches");
557                for (int i = 0; i < num; i++) {
558                        EntityLocation loc = pickHiddenLocation(random, data.system, 70f, null);
559                        String type = cacheTypes.pick();
560                        AddedEntity added = addEntity(random, data.system, loc, type, Factions.NEUTRAL);
561                        if (added != null) {
562                                data.generated.add(added);
563                        }
564                        
565                        if (DEBUG && added != null) System.out.println("      Added resource cache: " + type);
566                }
567        }
568        
569        public void addDebrisFields(StarSystemData data, float chanceToAddAny, int min, int max) {
570                addDebrisFields(data, chanceToAddAny, min, max, null, 0f, 0, 0);
571        }
572        public void addDebrisFields(StarSystemData data, float chanceToAddAny, int min, int max, String defFaction, float defProb, int minStr, int maxStr) {
573                if (random.nextFloat() >= chanceToAddAny) return;
574
575                int numDebrisFields = min + random.nextInt(max - min + 1);
576                if (DEBUG) System.out.println("    Adding up to " + numDebrisFields + " debris fields");
577                for (int i = 0; i < numDebrisFields; i++) {
578                        
579                        
580                        float radius = 150f + random.nextFloat() * 300f;
581                        EntityLocation loc = pickAnyLocation(random, data.system, radius + 100f, null);
582                        if (loc == null) continue;
583                        
584                        DebrisFieldParams params = new DebrisFieldParams(
585                                        radius, // field radius - should not go above 1000 for performance reasons
586                                        -1f, // density, visual - affects number of debris pieces
587                                        10000000f, // duration in days 
588                                        0f); // days the field will keep generating glowing pieces
589                        
590                        if (defFaction != null) {
591                                params.defFaction = defFaction;
592                                params.defenderProb = defProb;
593                                params.minStr = minStr;
594                                params.maxStr = maxStr;
595                        }
596                        
597                        params.source = DebrisFieldSource.GEN;
598                        SectorEntityToken debris = Misc.addDebrisField(data.system, params, random);
599                        setEntityLocation(debris, loc, Entities.DEBRIS_FIELD_SHARED);
600                        
601                        AddedEntity added = new AddedEntity(debris, loc, Entities.DEBRIS_FIELD_SHARED);
602                        data.generated.add(added);
603                        
604                        if (DEBUG) System.out.println("      Added debris field");
605                }
606        }
607        
608        public AddedEntity addDebrisField(StarSystemData data, SectorEntityToken focus, float radius) {
609                DebrisFieldParams params = new DebrisFieldParams(
610                                radius, // field radius - should not go above 1000 for performance reasons
611                                -1f, // density, visual - affects number of debris pieces
612                                10000000f, // duration in days 
613                                0f); // days the field will keep generating glowing pieces
614                
615                params.source = DebrisFieldSource.GEN;
616                SectorEntityToken debris = Misc.addDebrisField(focus.getContainingLocation(), params, random);
617                debris.setCircularOrbit(focus, 0, 0, 100f);
618                if (DEBUG) System.out.println("      Added debris field");
619                
620                EntityLocation loc = new EntityLocation();
621                loc.type = LocationType.OUTER_SYSTEM; // sigh
622                AddedEntity added = new AddedEntity(debris, loc, Entities.DEBRIS_FIELD_SHARED);
623                data.generated.add(added);
624                
625                return added;
626        }
627        
628        
629        public WeightedRandomPicker<String> createStringPicker(Object ... params) {
630                return createStringPicker(random, params);
631        }
632        
633        public static WeightedRandomPicker<String> createStringPicker(Random random, Object ... params) {
634                if (random == null) random = StarSystemGenerator.random;
635                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
636                for (int i = 0; i < params.length; i += 2) {
637                        String item = (String) params[i];
638                        float weight = 0f;
639                        if (params[i+1] instanceof Float) {
640                                weight = (Float) params[i+1];
641                        } else if (params[i+1] instanceof Integer) {
642                                weight = (Integer) params[i+1];
643                        }
644                        picker.add(item, weight);
645                }
646                return picker;
647        }
648        
649        
650        public void addDerelictShip(StarSystemData data, EntityLocation loc, WeightedRandomPicker<String> factions) {
651                if (loc == null) return;
652                
653//              WeightedRandomPicker<String> factionPicker = new WeightedRandomPicker<String>(random);
654//              for (String faction : factions) {
655//                      factionPicker.add(faction);
656//              }
657                String faction = factions.pick();
658                DerelictShipData params = DerelictShipEntityPlugin.createRandom(faction, null, random, DerelictShipEntityPlugin.getDefaultSModProb());
659                if (params != null) {
660                        CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) addSalvageEntity(random, data.system,
661                                                                                        Entities.WRECK, Factions.NEUTRAL, params);
662                        entity.setDiscoverable(true);
663                        setEntityLocation(entity, loc, Entities.WRECK);
664                        if (DEBUG) System.out.println("      Added ship: " + 
665                                        ((DerelictShipEntityPlugin)entity.getCustomPlugin()).getData().ship.variantId);
666                        
667                        AddedEntity added = new AddedEntity(entity, null, Entities.WRECK);
668                        data.generated.add(added);
669                }
670                
671        }
672        
673        public AddedEntity addDerelictShip(StarSystemData data, EntityLocation loc, String variantId) {
674                if (loc == null) return null;
675        
676                DerelictShipData params = DerelictShipEntityPlugin.createVariant(variantId, random, DerelictShipEntityPlugin.getDefaultSModProb());
677                if (params != null) {
678                        CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) addSalvageEntity(random, data.system,
679                                        Entities.WRECK, Factions.NEUTRAL, params);
680                        entity.setDiscoverable(true);
681                        setEntityLocation(entity, loc, Entities.WRECK);
682                        if (DEBUG) System.out.println("      Added ship: " + 
683                                        ((DerelictShipEntityPlugin)entity.getCustomPlugin()).getData().ship.variantId);
684                        
685                        AddedEntity added = new AddedEntity(entity, null, Entities.WRECK);
686                        data.generated.add(added);
687                        return added;
688                }
689                return null;
690                
691        }
692        
693        
694        public static EntityLocation pickCommonLocation(Random random, StarSystemAPI system, float gap, boolean allowStarOrbit, Set<SectorEntityToken> exclude) {
695                if (random == null) random = StarSystemGenerator.random;
696                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
697                weights.put(LocationType.PLANET_ORBIT, 10f);
698                if (allowStarOrbit) {
699                        weights.put(LocationType.STAR_ORBIT, 10f);
700                }
701                weights.put(LocationType.GAS_GIANT_ORBIT, 5f);
702                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, exclude, gap, weights);
703                if (locs.isEmpty()) {
704                        return pickAnyLocation(random, system, gap, exclude);
705                }
706                return locs.pick();
707        }
708        
709        public static EntityLocation pickUncommonLocation(Random random, StarSystemAPI system, float gap, Set<SectorEntityToken> exclude) {
710                if (random == null) random = StarSystemGenerator.random;
711                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
712                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
713                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
714                weights.put(LocationType.IN_RING, 5f);
715                weights.put(LocationType.IN_SMALL_NEBULA, 5f);
716                weights.put(LocationType.L_POINT, 5f);
717                weights.put(LocationType.GAS_GIANT_ORBIT, 5f);
718                weights.put(LocationType.JUMP_ORBIT, 5f);
719                weights.put(LocationType.NEAR_STAR, 5f);
720                weights.put(LocationType.OUTER_SYSTEM, 5f);
721                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, exclude, gap, weights);
722                if (locs.isEmpty()) {
723                        return pickAnyLocation(random, system, gap, exclude);
724                }
725                return locs.pick();
726        }
727        
728        public static EntityLocation pickAnyLocation(Random random, StarSystemAPI system, float gap, Set<SectorEntityToken> exclude) {
729                if (random == null) random = StarSystemGenerator.random;
730                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
731                weights.put(LocationType.PLANET_ORBIT, 10f);
732                weights.put(LocationType.STAR_ORBIT, 10f);
733                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
734                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
735                weights.put(LocationType.IN_RING, 5f);
736                weights.put(LocationType.IN_SMALL_NEBULA, 5f);
737                weights.put(LocationType.L_POINT, 5f);
738                weights.put(LocationType.GAS_GIANT_ORBIT, 5f);
739                weights.put(LocationType.JUMP_ORBIT, 5f);
740                weights.put(LocationType.NEAR_STAR, 5f);
741                weights.put(LocationType.OUTER_SYSTEM, 5f);
742                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, exclude, gap, weights);
743                return locs.pick();
744        }
745        
746        public static EntityLocation pickHiddenLocation(Random random, StarSystemAPI system, float gap, Set<SectorEntityToken> exclude) {
747                if (random == null) random = StarSystemGenerator.random;
748                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
749                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
750                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
751                weights.put(LocationType.IN_RING, 5f);
752                weights.put(LocationType.IN_SMALL_NEBULA, 5f);
753                weights.put(LocationType.L_POINT, 5f);
754                weights.put(LocationType.GAS_GIANT_ORBIT, 5f);
755                weights.put(LocationType.NEAR_STAR, 5f);
756                weights.put(LocationType.OUTER_SYSTEM, 5f);
757                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, exclude, gap, weights);
758                if (locs.isEmpty()) {
759                        return pickAnyLocation(random, system, gap, exclude);
760                }
761                return locs.pick();
762        }
763        
764        public static EntityLocation pickHiddenLocationNotNearStar(Random random, StarSystemAPI system, float gap, Set<SectorEntityToken> exclude) {
765                if (random == null) random = StarSystemGenerator.random;
766                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
767                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
768                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
769                weights.put(LocationType.IN_RING, 5f);
770                weights.put(LocationType.IN_SMALL_NEBULA, 5f);
771                weights.put(LocationType.L_POINT, 5f);
772                weights.put(LocationType.GAS_GIANT_ORBIT, 5f);
773                weights.put(LocationType.OUTER_SYSTEM, 5f);
774                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, exclude, gap, weights);
775                if (locs.isEmpty()) {
776                        return pickAnyLocation(random, system, gap, exclude);
777                }
778                return locs.pick();
779        }
780        
781        
782        
783        
784        
785        public static WeightedRandomPicker<EntityLocation> getLocations(Random random, StarSystemAPI system,
786                        float minGap, LinkedHashMap<LocationType, Float> weights) {
787                return getLocations(random, system, null, minGap, weights);
788        }
789        public static WeightedRandomPicker<EntityLocation> getLocations(Random random, StarSystemAPI system, Set<SectorEntityToken> exclude,
790                                                                                                                                        float minGap, LinkedHashMap<LocationType, Float> weights) {
791                if (random == null) random = StarSystemGenerator.random;
792                WeightedRandomPicker<EntityLocation> result = new WeightedRandomPicker<EntityLocation>(random);
793                
794                system.updateAllOrbits();
795                
796//              if (system.getType() == StarSystemType.TRINARY_1CLOSE_1FAR) {
797//                      System.out.println("fwfewfwe");
798//              }
799                
800                float inner = getInnerRadius(system);
801                float outer = getOuterRadius(system);
802                //outer += 1000f;
803                if (outer < 3000) outer = 3000;
804                if (outer > 25000) outer = 25000;
805                
806                StarSystemType systemType = system.getType();
807                
808                for (LocationType type : weights.keySet()) {
809                        float weight = weights.get(type);
810                        List<EntityLocation> locs = new ArrayList<EntityLocation>();
811                        switch (type) {
812                        case PLANET_ORBIT:
813                                for (PlanetAPI planet : system.getPlanets()) {
814                                        //if (planet.isMoon()) continue;
815                                        if (planet.isGasGiant()) continue;
816                                        if (planet.isStar()) continue;
817                                        if (exclude != null && exclude.contains(planet)) continue;
818                                        
819                                        float ow = getOrbitalRadius(planet);
820                                        List<OrbitGap> gaps = findGaps(planet, 100f, 100f + ow + minGap, minGap);
821                                        EntityLocation loc = createLocationAtRandomGap(random, planet, gaps, type);
822                                        if (loc != null) locs.add(loc);
823                                }
824                                break;
825                        case L_POINT:
826                                for (PlanetAPI planet : system.getPlanets()) {
827                                        if (planet.isStar()) continue;
828                                        if (planet.isMoon()) continue;
829                                        if (planet.getRadius() < 100) continue;
830                                        if (planet.getOrbit() == null || planet.getOrbit().getFocus() == null) continue;
831                                        if (planet.getCircularOrbitRadius() <= 0) continue;
832                                        for (LagrangePointType lpt : EnumSet.of(LagrangePointType.L4, LagrangePointType.L5)) {
833                                                float orbitRadius = planet.getCircularOrbitRadius();
834                                                float angleOffset = -StarSystemGenerator.LAGRANGE_OFFSET * 0.5f;
835                                                if (lpt == LagrangePointType.L5) angleOffset = StarSystemGenerator.LAGRANGE_OFFSET * 0.5f;
836                                                float angle = planet.getCircularOrbitAngle() + angleOffset;
837                                                Vector2f location = Misc.getUnitVectorAtDegreeAngle(angle);
838                                                location.scale(orbitRadius);
839                                                Vector2f.add(location, planet.getOrbit().getFocus().getLocation(), location);
840                                                
841                                                boolean clear = isAreaEmpty(system, location);
842                                                if (clear) {
843                                                        EntityLocation loc = new EntityLocation();
844                                                        loc.type = type;
845                                                        float orbitDays = planet.getCircularOrbitPeriod();
846//                                                      loc.orbit = Global.getFactory().createCircularOrbit(planet.getOrbitFocus(), 
847//                                                                                                                                              angle, orbitRadius, orbitDays);
848                                                        loc.orbit = Global.getFactory().createCircularOrbitWithSpin(planet.getOrbitFocus(), 
849                                                                                                angle, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
850                                                        locs.add(loc);
851                                                }
852                                        }
853                                }
854                                break;
855                        case GAS_GIANT_ORBIT:
856                                for (PlanetAPI planet : system.getPlanets()) {
857                                        if (planet.isStar()) continue;
858                                        if (!planet.isGasGiant()) continue;
859                                        if (exclude != null && exclude.contains(planet)) continue;
860                                        
861                                        float ow = getOrbitalRadius(planet);
862                                        List<OrbitGap> gaps = findGaps(planet, 100f, 100f + ow + minGap, minGap);
863                                        EntityLocation loc = createLocationAtRandomGap(random, planet, gaps, type);
864                                        if (loc != null) locs.add(loc);
865                                }
866                                break;
867                        case JUMP_ORBIT:
868                                List<SectorEntityToken> jumpPoints = system.getEntitiesWithTag(Tags.JUMP_POINT);
869                                for (SectorEntityToken point : jumpPoints) {
870                                        if (exclude != null && exclude.contains(point)) continue;
871                                        List<OrbitGap> gaps = findGaps(point, 200f, 200f + point.getRadius() + minGap, minGap);
872                                        EntityLocation loc = createLocationAtRandomGap(random, point, gaps, type);
873                                        if (loc != null) locs.add(loc);
874                                }
875                                break;
876                        case NEAR_STAR:
877                                if (systemType != StarSystemType.NEBULA) {
878                                        float r = system.getStar().getRadius();
879                                        float extra = 500f;
880                                        r += extra;
881                                        List<OrbitGap> gaps = findGaps(system.getStar(), 200f, 200f + r + minGap, minGap);
882                                        EntityLocation loc = createLocationAtRandomGap(random, system.getStar(), gaps, type);
883                                        if (loc != null) locs.add(loc);
884                                        
885                                        if (system.getSecondary() != null) {
886                                                r = system.getSecondary().getRadius();
887                                                gaps = findGaps(system.getSecondary(), 200f, 200f + r + minGap, minGap);
888                                                loc = createLocationAtRandomGap(random, system.getSecondary(), gaps, type);
889                                                if (loc != null) locs.add(loc);
890                                        }
891                                        if (system.getTertiary() != null) {
892                                                r = system.getTertiary().getRadius();
893                                                gaps = findGaps(system.getTertiary(), 200f, 200f + r + minGap, minGap);
894                                                loc = createLocationAtRandomGap(random, system.getTertiary(), gaps, type);
895                                                if (loc != null) locs.add(loc);
896                                        }
897                                }
898                                break;
899                        case IN_RING:
900                                for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
901                                        if (exclude != null && exclude.contains(terrain)) continue;
902                                        if (terrain.hasTag(Tags.ACCRETION_DISK)) continue;
903                                        CampaignTerrainPlugin plugin = terrain.getPlugin();
904                                        if (plugin instanceof RingSystemTerrainPlugin) {
905                                                RingSystemTerrainPlugin ring = (RingSystemTerrainPlugin) plugin;
906                                                float start = ring.params.middleRadius - ring.params.bandWidthInEngine / 2f;
907                                                List<OrbitGap> gaps = findGaps(terrain, 
908                                                                                          start - 100f, start + ring.params.bandWidthInEngine + 100f, minGap);
909                                                EntityLocation loc = createLocationAtRandomGap(random, terrain, gaps, type);
910                                                if (loc != null) locs.add(loc);
911                                        }
912                                }
913                                break;
914                        case IN_SMALL_NEBULA:
915                                for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
916                                        if (exclude != null && exclude.contains(terrain)) continue;
917                                        CampaignTerrainPlugin plugin = terrain.getPlugin();
918                                        if (plugin instanceof NebulaTerrainPlugin) {
919                                                NebulaTerrainPlugin nebula = (NebulaTerrainPlugin) plugin;
920                                                float tilesHigh = nebula.getTiles()[0].length;
921                                                float tilesWide = nebula.getTiles().length;
922                                                float ts = nebula.getTileSize();
923                                                float w = ts * tilesWide;
924                                                float h = ts * tilesHigh;
925                                                if (w <= 10000) {
926                                                        float r = (float) Math.sqrt(w * w + h * h);
927                                                        if (terrain.getOrbit() == null) {
928                                                                Vector2f point = Misc.getPointWithinRadius(terrain.getLocation(), r * 0.5f, random);
929                                                                EntityLocation loc = new EntityLocation();
930                                                                loc.type = type;
931                                                                loc.location = point;
932                                                                loc.orbit = null;
933                                                                locs.add(loc);
934                                                        } else {
935                                                                float min = Math.min(100f, r * 0.25f);
936                                                                float max = r;
937                                                                EntityLocation loc = new EntityLocation();
938                                                                loc.type = type;
939                                                                float orbitRadius = min + (max - min) * (0.75f * random.nextFloat());
940                                                                float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
941                                                                loc.orbit = Global.getFactory().createCircularOrbitWithSpin(terrain, 
942                                                                                random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
943                                                                locs.add(loc);
944                                                        }
945                                                }
946                                        }
947                                }
948                                break;
949                        case IN_ASTEROID_BELT:
950                                for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
951                                        if (exclude != null && exclude.contains(terrain)) continue;
952                                        CampaignTerrainPlugin plugin = terrain.getPlugin();
953                                        if (plugin instanceof AsteroidBeltTerrainPlugin && !(plugin instanceof AsteroidFieldTerrainPlugin)) {
954                                                AsteroidBeltTerrainPlugin ring = (AsteroidBeltTerrainPlugin) plugin;
955                                                if (ring.params != null) {
956                                                        float start = ring.params.middleRadius - ring.params.bandWidthInEngine / 2f;
957                                                        List<OrbitGap> gaps = findGaps(terrain, 
958                                                                                                  start - 100f, start + ring.params.bandWidthInEngine + 100f, minGap);
959                                                        EntityLocation loc = createLocationAtRandomGap(random, terrain, gaps, type);
960                                                        if (loc != null) locs.add(loc);
961                                                } else {
962                                                        //System.out.println("egaegfwgwgew");
963                                                }
964                                        }
965                                }
966                                break;
967                        case IN_ASTEROID_FIELD:
968                                for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
969                                        if (exclude != null && exclude.contains(terrain)) continue;
970                                        CampaignTerrainPlugin plugin = terrain.getPlugin();
971                                        if (plugin instanceof AsteroidFieldTerrainPlugin) {
972                                                AsteroidFieldTerrainPlugin ring = (AsteroidFieldTerrainPlugin) plugin;
973                                                if (isAreaEmpty(system, terrain.getLocation())) {
974                                                        float min = Math.min(100f, ring.params.bandWidthInEngine * 0.25f);
975                                                        float max = ring.params.bandWidthInEngine;
976                                                        EntityLocation loc = new EntityLocation();
977                                                        loc.type = type;
978                                                        float orbitRadius = min + (max - min) * (0.75f * random.nextFloat());
979                                                        float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
980//                                                      loc.orbit = Global.getFactory().createCircularOrbit(terrain, 
981//                                                                                      random.nextFloat() * 360f, orbitRadius, orbitDays);
982                                                        loc.orbit = Global.getFactory().createCircularOrbitWithSpin(terrain, 
983                                                                        random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
984                                                        locs.add(loc);
985                                                        
986                                                }
987                                        }
988                                }
989                                break;
990                        case OUTER_SYSTEM:
991                                SectorEntityToken near = pickOuterEntityToSpawnNear(random, system);
992//                              if (!USE_NEW_SPAWN && near != null) {
993//                                      EntityLocation loc = new EntityLocation();
994//                                      loc.type = type;
995//                                      float orbitRadius = 3000 + 1500f * random.nextFloat();
996//                                      float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
997//                                      loc.orbit = Global.getFactory().createCircularOrbitWithSpin(near, 
998//                                                      random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
999//                                      locs.add(loc);
1000//                              } else {
1001                                        if (near != null && near.getCircularOrbitRadius() > 0) {
1002                                                EntityLocation loc = new EntityLocation();
1003                                                loc.type = type;
1004        //                                      float orbitRadius = 3000 + 1500f * random.nextFloat();
1005        //                                      float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
1006        ////                                    loc.orbit = Global.getFactory().createCircularOrbitWithSpin(system.getCenter(), 
1007        ////                                                    random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
1008        //                                      loc.orbit = Global.getFactory().createCircularOrbitWithSpin(near, 
1009        //                                                      random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
1010                                                float orbitRadius = near.getCircularOrbitRadius() + 1000f + 1500f * random.nextFloat();
1011                                                float orbitDays = near.getCircularOrbitPeriod();
1012                                                loc.orbit = Global.getFactory().createCircularOrbitWithSpin(system.getCenter(), 
1013                                                                near.getCircularOrbitAngle() + 15f - 30f * random.nextFloat(),
1014                                                                orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);                                 
1015                                                locs.add(loc);
1016                                        } else if (near != null) {
1017                                                EntityLocation loc = new EntityLocation();
1018                                                loc.type = type;
1019                                                float orbitRadius = outer + 500f + 500f * random.nextFloat();
1020                                                float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
1021                                                loc.orbit = Global.getFactory().createCircularOrbitWithSpin(system.getCenter(), 
1022                                                                random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
1023                                                locs.add(loc);
1024                                        }
1025//                              }
1026                                break;
1027                        case STAR_ORBIT:
1028//                              if (system.getType() == StarSystemType.TRINARY_1CLOSE_1FAR) {
1029//                                      System.out.println("fwfewfwe");
1030//                              }
1031                                        
1032                                SectorEntityToken main = system.getCenter();
1033                                List<SectorEntityToken> secondary = new ArrayList<SectorEntityToken>();
1034                                switch (system.getType()) {
1035                                case BINARY_FAR:
1036                                        secondary.add(system.getSecondary());
1037                                        break;
1038                                case TRINARY_1CLOSE_1FAR:
1039                                        secondary.add(system.getTertiary());
1040                                        break;
1041                                case TRINARY_2FAR:
1042                                        secondary.add(system.getSecondary());
1043                                        secondary.add(system.getTertiary());
1044                                        break;
1045                                }
1046                                
1047                                if (main != null) {
1048                                        List<OrbitGap> gaps = findGaps(main, inner, outer + minGap, minGap);
1049                                        EntityLocation loc;
1050                                        for (OrbitGap gap : gaps) {
1051                                                loc = createLocationAtGap(random, main, gap, type);
1052                                                if (loc != null) locs.add(loc);
1053                                        }
1054                                }
1055                                
1056                                for (SectorEntityToken star : secondary) {
1057                                        float ow = getOrbitalRadius((PlanetAPI) star);
1058                                        if (ow < 3000) ow = 3000;
1059                                        float r = star.getRadius();
1060                                        List<OrbitGap> gaps = findGaps(star, r, ow + r + minGap, minGap);
1061                                        EntityLocation loc; 
1062                                        for (OrbitGap gap : gaps) {
1063                                                loc = createLocationAtGap(random, star, gap, type);
1064                                                if (loc != null) locs.add(loc);
1065                                        }
1066                                }
1067                                
1068                                break;
1069                        }
1070                        
1071                        // if in nebula, convert circular orbits to fixed locations
1072                        if (system.getType() == StarSystemType.NEBULA) {
1073                                for (EntityLocation loc : locs) {
1074                                        if (loc.orbit != null && loc.orbit.getFocus() == system.getCenter()) {
1075                                                loc.location = loc.orbit.computeCurrentLocation();
1076                                                loc.orbit = null;
1077                                        }
1078                                }
1079                        }
1080                        
1081                        if (!locs.isEmpty()) {
1082                                float weightPer = weight / (float) locs.size();
1083                                for (EntityLocation loc : locs) {
1084                                        result.add(loc, weightPer);
1085                                }
1086                        }
1087                        
1088                }
1089                
1090                return result;
1091        }
1092        
1093        public static EntityLocation createLocationAtRandomGap(Random random, SectorEntityToken center, float minGap) {
1094                if (random == null) random = StarSystemGenerator.random;
1095                float ow = getOrbitalRadius(center);
1096                List<OrbitGap> gaps = findGaps(center, 100f, 100f + ow + minGap, minGap);
1097                EntityLocation loc = createLocationAtRandomGap(random, center, gaps, LocationType.PLANET_ORBIT);
1098                return loc;
1099        }
1100        
1101        
1102        private static EntityLocation createLocationAtRandomGap(Random random, SectorEntityToken center, List<OrbitGap> gaps, LocationType type) {
1103                if (gaps.isEmpty()) return null;
1104                if (random == null) random = StarSystemGenerator.random;
1105                WeightedRandomPicker<OrbitGap> picker = new WeightedRandomPicker<OrbitGap>(random);
1106                picker.addAll(gaps);
1107                OrbitGap gap = picker.pick();
1108                return createLocationAtGap(random, center, gap, type);
1109        }
1110        
1111        private static EntityLocation createLocationAtGap(Random random, SectorEntityToken center, OrbitGap gap, LocationType type) {
1112                if (gap != null) {
1113                        if (random == null) random = StarSystemGenerator.random;
1114                        EntityLocation loc = new EntityLocation();
1115                        loc.type = type;
1116                        float orbitRadius = gap.start + (gap.end - gap.start) * (0.25f + 0.5f * random.nextFloat());
1117                        float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
1118//                      loc.orbit = Global.getFactory().createCircularOrbit(center, 
1119//                                      random.nextFloat() * 360f, orbitRadius, orbitDays);
1120                        loc.orbit = Global.getFactory().createCircularOrbitWithSpin(center, 
1121                                        random.nextFloat() * 360f, orbitRadius, orbitDays, random.nextFloat() * 10f + 1f);
1122                        return loc;
1123                }
1124                return null;
1125        }
1126        
1127        
1128        public static class OrbitGap {
1129                public float start;
1130                public float end;
1131        }
1132        
1133        public static class OrbitItem {
1134                public SectorEntityToken item;
1135                public float orbitRadius;
1136                public float orbitalWidth;
1137        }
1138        
1139        public static List<OrbitGap> findGaps(SectorEntityToken center, float minPad, float maxDist, float minGap) {
1140                List<OrbitGap> gaps = new ArrayList<OrbitGap>();
1141                
1142                LocationAPI loc = center.getContainingLocation();
1143                if (loc == null) return gaps;
1144                
1145                List<OrbitItem> items = new ArrayList<OrbitItem>();
1146                for (PlanetAPI planet : loc.getPlanets()) {
1147                        if (planet.getOrbitFocus() != center) continue;
1148                        
1149                        OrbitItem item = new OrbitItem();
1150                        item.item = planet;
1151                        item.orbitRadius = planet.getCircularOrbitRadius();
1152                        if (item.orbitRadius > maxDist) continue;
1153                        
1154                        item.orbitalWidth = getOrbitalRadius(planet) * 2f;
1155                        items.add(item);
1156                }
1157                
1158                for (CampaignTerrainAPI terrain : loc.getTerrainCopy()) {
1159                        if (terrain.getOrbitFocus() != center) continue;
1160                        
1161                        CampaignTerrainPlugin plugin = terrain.getPlugin();
1162                        if (plugin instanceof StarCoronaTerrainPlugin) continue;
1163                        if (plugin instanceof MagneticFieldTerrainPlugin) continue;
1164                        if (plugin instanceof PulsarBeamTerrainPlugin) continue;
1165                        
1166                        if (plugin instanceof BaseRingTerrain) {
1167                                BaseRingTerrain ring = (BaseRingTerrain) plugin;
1168                                
1169                                OrbitItem item = new OrbitItem();
1170                                item.item = terrain;
1171                                item.orbitRadius = ring.params.middleRadius;
1172                                if (item.orbitRadius > maxDist) continue;
1173                                
1174                                item.orbitalWidth = ring.params.bandWidthInEngine;
1175                                items.add(item);
1176                        }
1177                }
1178                
1179                List<CustomCampaignEntityAPI> entities = loc.getEntities(CustomCampaignEntityAPI.class);
1180                for (SectorEntityToken custom : entities) {
1181                        if (custom.getOrbitFocus() != center) continue;
1182                        
1183                        OrbitItem item = new OrbitItem();
1184                        item.item = custom;
1185                        item.orbitRadius = custom.getCircularOrbitRadius();
1186                        if (item.orbitRadius > maxDist) continue;
1187                        
1188                        item.orbitalWidth = custom.getRadius() * 2f;
1189                        items.add(item);
1190                }
1191                
1192                //List<SectorEntityToken> jumpPoints = loc.getEntitiesWithTag(Tags.JUMP_POINT);
1193                List<SectorEntityToken> jumpPoints = loc.getJumpPoints();
1194                for (SectorEntityToken point : jumpPoints) {
1195                        if (point.getOrbitFocus() != center) continue;
1196                        
1197                        OrbitItem item = new OrbitItem();
1198                        item.item = point;
1199                        item.orbitRadius = point.getCircularOrbitRadius();
1200                        if (item.orbitRadius > maxDist) continue;
1201                        
1202                        item.orbitalWidth = point.getRadius() * 2f;
1203                        items.add(item);
1204                }
1205                
1206                Collections.sort(items, new Comparator<OrbitItem>() {
1207                        public int compare(OrbitItem o1, OrbitItem o2) {
1208                                return (int)Math.signum(o1.orbitRadius - o2.orbitRadius);
1209                        }
1210                });
1211                
1212                float prev = center.getRadius() + minPad;
1213                for (OrbitItem item : items) {
1214                        float next = item.orbitRadius - item.orbitalWidth / 2f;
1215                        if (next - prev >= minGap) {
1216                                OrbitGap gap = new OrbitGap();
1217                                gap.start = prev;
1218                                gap.end = next;
1219                                gaps.add(gap);
1220                        }
1221                        prev = Math.max(prev, item.orbitRadius + item.orbitalWidth / 2f);
1222                }
1223                
1224                if (maxDist - prev >= minGap) {
1225                        OrbitGap gap = new OrbitGap();
1226                        gap.start = prev;
1227                        gap.end = maxDist;
1228                        gaps.add(gap);
1229                }
1230                
1231                return gaps;
1232        }
1233        
1234        
1235        public static float getInnerRadius(StarSystemAPI system) {
1236                switch (system.getType()) {
1237                case NEBULA:
1238                        return 0;
1239                case DEEP_SPACE:
1240                        return 500f;
1241                case SINGLE:
1242                case BINARY_FAR:
1243                case TRINARY_2FAR:
1244                        if (system.getStar() == null) return 0; // alpha site
1245                        return system.getStar().getRadius();
1246                case BINARY_CLOSE:
1247                case TRINARY_1CLOSE_1FAR:
1248                        return Math.max(system.getStar().getCircularOrbitRadius() + system.getStar().getRadius(),
1249                                                        system.getSecondary().getCircularOrbitRadius() + system.getSecondary().getRadius());
1250                case TRINARY_2CLOSE:
1251                        float max = Math.max(system.getStar().getCircularOrbitRadius() + system.getStar().getRadius(),
1252                                                             system.getSecondary().getCircularOrbitRadius() + system.getSecondary().getRadius());
1253                        max = Math.max(max,
1254                                                   system.getTertiary().getCircularOrbitRadius() + system.getTertiary().getRadius());
1255                        return max;
1256                }
1257                return 0;
1258        }
1259        
1260        
1261        public static SectorEntityToken pickOuterEntityToSpawnNear(Random random, StarSystemAPI system) {
1262                if (random == null) random = StarSystemGenerator.random;
1263                
1264                WeightedRandomPicker<SectorEntityToken> picker = new WeightedRandomPicker<SectorEntityToken>(random);
1265                float max = getOuterRadius(system);
1266                float threshold = max * 0.75f;
1267                
1268                for (PlanetAPI planet : system.getPlanets()) {
1269                        float r = planet.getLocation().length() + getOrbitalRadius(planet);
1270                        if (r > threshold) {
1271                                picker.add(planet);
1272                        }
1273                }
1274                
1275//              for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
1276//                      CampaignTerrainPlugin plugin = terrain.getPlugin();
1277//                      if (plugin instanceof BaseRingTerrain && !(plugin instanceof PulsarBeamTerrainPlugin)) {
1278//                              BaseRingTerrain ring = (BaseRingTerrain) plugin;
1279//                              float r = ring.params.middleRadius + ring.params.bandWidthInEngine * 0.5f;
1280//                              r += Misc.getDistance(system.getCenter().getLocation(), terrain.getLocation());
1281//                              if (r > threshold) {
1282//                                      picker.add(terrain);
1283//                              }
1284//                      } else if (plugin instanceof BaseTiledTerrain) {
1285//                              if (plugin instanceof NebulaTerrainPlugin) continue;
1286//                              
1287//                              BaseTiledTerrain tiles = (BaseTiledTerrain) plugin;
1288//                              float r = tiles.getRenderRange();
1289//                              r += Misc.getDistance(system.getCenter().getLocation(), terrain.getLocation());
1290//                              if (r > threshold) {
1291//                                      picker.add(terrain);
1292//                              }
1293//                      }
1294//              }
1295                
1296                List<SectorEntityToken> jumpPoints = system.getEntitiesWithTag(Tags.JUMP_POINT);
1297                for (SectorEntityToken point : jumpPoints) {
1298                        float r = Misc.getDistance(system.getCenter().getLocation(), point.getLocation());
1299                        r += point.getRadius();
1300                        if (r > threshold) {
1301                                picker.add(point);
1302                        }
1303                }
1304                
1305                return picker.pick();
1306        }
1307        
1308        
1309        public static float getOuterRadius(StarSystemAPI system) {
1310                float max = 0f;
1311                
1312                for (PlanetAPI planet : system.getPlanets()) {
1313                        //float r = planet.getCircularOrbitRadius() + getOrbitalRadius(planet);
1314                        float r = planet.getLocation().length() + getOrbitalRadius(planet);
1315                        if (r > max) max = r;
1316                }
1317                
1318                for (CampaignTerrainAPI terrain : system.getTerrainCopy()) {
1319                        CampaignTerrainPlugin plugin = terrain.getPlugin();
1320                        
1321                        if (plugin instanceof BaseRingTerrain && !(plugin instanceof PulsarBeamTerrainPlugin)) {
1322                                BaseRingTerrain ring = (BaseRingTerrain) plugin;
1323                                float r = ring.params.middleRadius + ring.params.bandWidthInEngine * 0.5f;
1324                                r += Misc.getDistance(system.getCenter().getLocation(), terrain.getLocation());
1325                                if (r > max) max = r;
1326                        } else if (plugin instanceof BaseTiledTerrain) {
1327                                if (plugin instanceof NebulaTerrainPlugin) continue;
1328                                
1329                                BaseTiledTerrain tiles = (BaseTiledTerrain) plugin;
1330                                float r = tiles.getRenderRange();
1331                                r += Misc.getDistance(system.getCenter().getLocation(), terrain.getLocation());
1332                                if (r > max) max = r;
1333                        }
1334                }
1335                
1336//              List<CustomCampaignEntityAPI> entities = system.getEntities(CustomCampaignEntityAPI.class);
1337//              for (SectorEntityToken custom : entities) {
1338//                      //float r = custom.getCircularOrbitRadius() + custom.getRadius();
1339//                      float r = Misc.getDistance(system.getCenter().getLocation(), custom.getLocation());
1340//                      r += custom.getRadius();
1341//                      if (r > max) max = r;
1342//              }
1343                
1344                List<SectorEntityToken> jumpPoints = system.getEntitiesWithTag(Tags.JUMP_POINT);
1345                for (SectorEntityToken point : jumpPoints) {
1346                        float r = Misc.getDistance(system.getCenter().getLocation(), point.getLocation());
1347                        r += point.getRadius();
1348                        if (r > max) max = r;
1349                }
1350                
1351                return max;
1352        }
1353        
1354        public static boolean isAreaEmpty(LocationAPI loc, Vector2f coords) {
1355                //loc.updateAllOrbits();
1356                
1357                float range = 400f;
1358                for (PlanetAPI planet : loc.getPlanets()) {
1359                        float dist = Misc.getDistance(planet.getLocation(), coords);
1360                        if (dist < range + planet.getRadius()) return false;
1361                }
1362                
1363                List<CustomCampaignEntityAPI> entities = loc.getEntities(CustomCampaignEntityAPI.class);
1364                for (SectorEntityToken custom : entities) {
1365                        float dist = Misc.getDistance(custom.getLocation(), coords);
1366                        if (dist < range + custom.getRadius()) {
1367                                return false;
1368                        }
1369                }
1370                
1371                for (CampaignTerrainAPI terrain : loc.getTerrainCopy()) {
1372                        CampaignTerrainPlugin plugin = terrain.getPlugin();
1373//                      if (plugin instanceof PulsarBeamTerrainPlugin) continue;
1374//                      if (plugin instanceof HyperspaceTerrainPlugin) continue;
1375//                      if (plugin instanceof NebulaTerrainPlugin) continue;
1376//                      if (plugin instanceof BaseRingTerrain) {
1377                        if (plugin instanceof DebrisFieldTerrainPlugin) {
1378                                DebrisFieldTerrainPlugin ring = (DebrisFieldTerrainPlugin) plugin;
1379                                float r = ring.params.middleRadius + ring.params.bandWidthInEngine * 0.5f;
1380                                float dist = Misc.getDistance(terrain.getLocation(), coords);
1381                                if (dist < range + r) return false;
1382                        }
1383                }
1384                
1385                return true;
1386        }
1387        
1388        public static float getOrbitalRadius(SectorEntityToken center) {
1389                LocationAPI loc = center.getContainingLocation();
1390                if (loc == null) return center.getRadius();
1391                
1392                float max = center.getRadius();
1393                for (PlanetAPI planet : loc.getPlanets()) {
1394                        if (planet.getOrbitFocus() != center) continue;
1395                        float r = planet.getCircularOrbitRadius() + getOrbitalRadius(planet);
1396                        if (r > max) max = r;
1397                }
1398                
1399                for (CampaignTerrainAPI terrain : loc.getTerrainCopy()) {
1400                        if (terrain.getOrbitFocus() != center) continue;
1401                        CampaignTerrainPlugin plugin = terrain.getPlugin();
1402                        
1403                        if (plugin instanceof PulsarBeamTerrainPlugin) continue;
1404                        if (plugin instanceof RadioChatterTerrainPlugin) continue;
1405                        
1406                        if (plugin instanceof BaseRingTerrain) {
1407                                BaseRingTerrain ring = (BaseRingTerrain) plugin;
1408                                float r = ring.params.middleRadius + ring.params.bandWidthInEngine * 0.5f;
1409                                if (r > max) max = r;
1410                        }
1411                }
1412                
1413                List<CustomCampaignEntityAPI> entities = loc.getEntities(CustomCampaignEntityAPI.class);
1414                for (SectorEntityToken custom : entities) {
1415                        if (custom.getOrbitFocus() != center) continue;
1416                        float r = custom.getCircularOrbitRadius() + custom.getRadius();
1417                        if (r > max) max = r;
1418                }
1419                
1420                return max;
1421        }
1422        
1423        public static AddedEntity addEntity(Random random, StarSystemAPI system, WeightedRandomPicker<EntityLocation> locs, String type, String faction) {
1424                EntityLocation loc = locs.pickAndRemove();
1425                return addEntity(random, system, loc, type, faction);
1426        }
1427        
1428        public static AddedEntity addNonSalvageEntity(LocationAPI system, EntityLocation loc, String type, String faction) {
1429                if (loc != null) {
1430                        SectorEntityToken entity = system.addCustomEntity(null, null, type, faction);
1431                        if (loc.orbit != null) {
1432                                entity.setOrbit(loc.orbit);
1433                                loc.orbit.setEntity(entity);
1434                        } else {
1435                                entity.setOrbit(null);
1436                                entity.getLocation().set(loc.location);
1437                        }
1438                        AddedEntity data = new AddedEntity(entity, loc, type);
1439                        return data;
1440                }
1441                return null;
1442        }
1443        
1444        public static AddedEntity addEntityAutoDetermineType(Random random, LocationAPI system, EntityLocation loc, String type, String faction) {
1445                if (SalvageEntityGeneratorOld.hasSalvageSpec(type)) {
1446                        return addEntity(random, system, loc, type, faction);
1447                } else {
1448                        return addNonSalvageEntity(system, loc, type, faction);
1449                }
1450        }
1451        
1452        public static AddedEntity addEntity(Random random, LocationAPI system, EntityLocation loc, String type, String faction) {
1453                if (loc != null) {
1454                        if (random == null) random = StarSystemGenerator.random;
1455                        SectorEntityToken entity = addSalvageEntity(random, system, type, faction);
1456                        if (loc.orbit != null) {
1457                                entity.setOrbit(loc.orbit);
1458                                loc.orbit.setEntity(entity);
1459                        } else {
1460                                entity.setOrbit(null);
1461                                entity.getLocation().set(loc.location);
1462                        }
1463                        AddedEntity data = new AddedEntity(entity, loc, type);
1464                        return data;
1465                }
1466                return null;
1467        }
1468        
1469        public static AddedEntity setEntityLocation(SectorEntityToken entity, EntityLocation loc, String type) {
1470                if (loc != null) {
1471                        if (loc.orbit != null) {
1472                                entity.setOrbit(loc.orbit);
1473                                loc.orbit.setEntity(entity);
1474                        } else {
1475                                entity.setOrbit(null);
1476                                entity.getLocation().set(loc.location);
1477                        }
1478                        AddedEntity data = new AddedEntity(entity, loc, type);
1479                        return data;
1480                }
1481                return null;
1482        }
1483        
1484        public static SectorEntityToken addSalvageEntity(LocationAPI location, String id, String faction) {
1485                return addSalvageEntity(null, location, id, faction);
1486        }
1487        public static SectorEntityToken addSalvageEntity(Random random, LocationAPI location, String id, String faction) {
1488                return addSalvageEntity(random, location, id, faction, null);
1489        }
1490        public static SectorEntityToken addSalvageEntity(LocationAPI location, String id, String faction, Object pluginParams) {
1491                return addSalvageEntity(null, location, id, faction, pluginParams);
1492        }
1493        public static SectorEntityToken addSalvageEntity(Random random, LocationAPI location, String id, String faction, Object pluginParams) {
1494                if (random == null) random = StarSystemGenerator.random;
1495                SalvageEntityGenDataSpec spec = SalvageEntityGeneratorOld.getSalvageSpec(id);
1496                
1497                CustomCampaignEntityAPI entity = location.addCustomEntity(null, spec.getNameOverride(), id, faction, pluginParams);
1498                
1499                if (spec.getRadiusOverride() > 0) {
1500                        entity.setRadius(spec.getRadiusOverride());
1501                }
1502                
1503                switch (spec.getType()) {
1504                case ALWAYS_VISIBLE:
1505                        entity.setSensorProfile(null);
1506                        entity.setDiscoverable(null);
1507                        break;
1508                case DISCOVERABLE:
1509                        entity.setSensorProfile(1f);
1510                        entity.setDiscoverable(true);
1511                        break;
1512                case NOT_DISCOVERABLE:
1513                        entity.setSensorProfile(1f);
1514                        entity.setDiscoverable(false);
1515                        break;
1516                }
1517                
1518                long seed = random.nextLong();
1519                entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, seed);
1520                
1521                entity.getDetectedRangeMod().modifyFlat("gen", spec.getDetectionRange());
1522                
1523                return entity;
1524        }
1525        
1526        
1527        public static CargoAPI genCargoFromDrop(SectorEntityToken entity) {
1528                MemoryAPI memory = entity.getMemoryWithoutUpdate();
1529                long seed = memory.getLong(MemFlags.SALVAGE_SEED);
1530                Random random = Misc.getRandom(seed, 1);
1531                
1532                List<DropData> dropValue = new ArrayList<DropData>(entity.getDropValue());
1533                List<DropData> dropRandom = new ArrayList<DropData>(entity.getDropRandom());
1534                SalvageEntityGenDataSpec spec = (SalvageEntityGenDataSpec) Global.getSettings().getSpec(
1535                                                                        SalvageEntityGenDataSpec.class, entity.getCustomEntityType(), true);
1536                
1537                if (spec != null) {
1538                        dropValue.addAll(spec.getDropValue());
1539                        dropRandom.addAll(spec.getDropRandom());
1540                }
1541                
1542                CargoAPI salvage = SalvageEntity.generateSalvage(random, 1f, 1f, 1f, 1f, dropValue, dropRandom);
1543                return salvage;
1544        }
1545        
1546        
1547        public static StarSystemData computeSystemData(StarSystemAPI system) {
1548                StarSystemData data = new StarSystemData();
1549                data.system = system;
1550                
1551                for (PlanetAPI planet : system.getPlanets()) {
1552                        if (planet.isStar()) {
1553                                data.stars.add(planet);
1554                        } else {
1555                                data.planets.add(planet);
1556                        }
1557                        
1558                        if (planet.isGasGiant()) {
1559                                data.gasGiants.add(planet);
1560                        }
1561                        
1562                        if (planet.getMarket() != null && planet.getMarket().isPlanetConditionMarketOnly()) {
1563                                MarketAPI market = planet.getMarket();
1564                                if (market.hasCondition(Conditions.HABITABLE)) {
1565                                        data.habitable.add(planet);
1566                                }
1567                                
1568                                for (String conditionId : DerelictThemeGenerator.interestingConditionsWithoutHabitable) {
1569                                        if (market.hasCondition(conditionId)) {
1570                                                data.resourceRich.add(planet);
1571                                                break;
1572                                        }
1573                                }
1574                                
1575                        }
1576                }
1577                
1578                return data;
1579        }
1580        
1581        public static void clearRuins(MarketAPI market) {
1582                if (market == null) return;
1583                
1584                market.removeCondition(Conditions.RUINS_EXTENSIVE);
1585                market.removeCondition(Conditions.RUINS_SCATTERED);
1586                market.removeCondition(Conditions.RUINS_VAST);
1587                market.removeCondition(Conditions.RUINS_WIDESPREAD);
1588                market.removeCondition(Conditions.DECIVILIZED);
1589        }
1590        
1591        public static void convertOrbitPointingDown(SectorEntityToken entity) {
1592                SectorEntityToken focus = entity.getOrbitFocus();
1593                if (focus != null) {
1594                        float angle = entity.getCircularOrbitAngle();
1595                        float period = entity.getCircularOrbitPeriod();
1596                        float radius = entity.getCircularOrbitRadius();
1597                        entity.setCircularOrbitPointingDown(focus, angle, radius, period);
1598                }
1599        }
1600        
1601        public static void convertOrbitNoSpin(SectorEntityToken entity) {
1602                convertOrbitNoSpin(entity, 90f);
1603        }
1604        public static void convertOrbitNoSpin(SectorEntityToken entity, float facing) {
1605                SectorEntityToken focus = entity.getOrbitFocus();
1606                if (focus != null) {
1607                        float angle = entity.getCircularOrbitAngle();
1608                        float period = entity.getCircularOrbitPeriod();
1609                        float radius = entity.getCircularOrbitRadius();
1610                        entity.setCircularOrbit(focus, angle, radius, period);
1611                        entity.setFacing(facing);
1612                }
1613        }
1614        
1615        public static void convertOrbitWithSpin(SectorEntityToken entity, float spin) {
1616                SectorEntityToken focus = entity.getOrbitFocus();
1617                if (focus != null) {
1618                        float angle = entity.getCircularOrbitAngle();
1619                        float period = entity.getCircularOrbitPeriod();
1620                        float radius = entity.getCircularOrbitRadius();
1621                        entity.setCircularOrbitWithSpin(focus, angle, radius, period, spin, spin);
1622                        ((CircularOrbitWithSpinAPI) entity.getOrbit()).setSpinVel(spin);
1623                }
1624        }
1625        
1626        
1627        public Random getRandom() {
1628                return random;
1629        }
1630        public void setRandom(Random random) {
1631                this.random = random;
1632        }
1633        
1634        
1635        /**
1636         * Sorted by *descending* distance from sortFrom.
1637         * @param context
1638         * @param sortFrom
1639         * @return
1640         */
1641        protected List<Constellation> getSortedAvailableConstellations(ThemeGenContext context, boolean emptyOk, final Vector2f sortFrom, List<Constellation> exclude) {
1642                List<Constellation> constellations = new ArrayList<Constellation>();
1643                for (Constellation c : context.constellations) {
1644                        if (context.majorThemes.containsKey(c)) continue;
1645                        if (!emptyOk && constellationIsEmpty(c)) continue;
1646                        
1647                        constellations.add(c);
1648                }
1649                
1650                if (exclude != null) {
1651                        constellations.removeAll(exclude);
1652                }
1653                
1654                Collections.sort(constellations, new Comparator<Constellation>() {
1655                        public int compare(Constellation o1, Constellation o2) {
1656                                float d1 = Misc.getDistance(o1.getLocation(), sortFrom);
1657                                float d2 = Misc.getDistance(o2.getLocation(), sortFrom);
1658                                return (int) Math.signum(d2 - d1);
1659                        }
1660                });
1661                return constellations;
1662        }
1663        
1664        public static boolean constellationIsEmpty(Constellation c) {
1665                for (StarSystemAPI s : c.getSystems()) {
1666                        if (!systemIsEmpty(s)) return false;
1667                }
1668                return true;
1669        }
1670        public static boolean systemIsEmpty(StarSystemAPI system) {
1671                for (PlanetAPI p : system.getPlanets()) {
1672                        if (!p.isStar()) return false;
1673                }
1674                //system.getTerrainCopy().isEmpty()
1675                return true;
1676        }
1677        
1678}
1679
1680
1681
1682
1683
1684
1685
1686
1687