001package com.fs.starfarer.api.impl.campaign.procgen;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.EnumSet;
007import java.util.LinkedHashMap;
008import java.util.LinkedHashSet;
009import java.util.List;
010import java.util.Map;
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.JumpPointAPI;
019import com.fs.starfarer.api.campaign.LocationAPI;
020import com.fs.starfarer.api.campaign.PlanetAPI;
021import com.fs.starfarer.api.campaign.PlanetSpecAPI;
022import com.fs.starfarer.api.campaign.SectorAPI;
023import com.fs.starfarer.api.campaign.SectorEntityToken;
024import com.fs.starfarer.api.campaign.StarSystemAPI;
025import com.fs.starfarer.api.characters.FullName.Gender;
026import com.fs.starfarer.api.impl.campaign.ids.Entities;
027import com.fs.starfarer.api.impl.campaign.ids.Factions;
028import com.fs.starfarer.api.impl.campaign.ids.Tags;
029import com.fs.starfarer.api.impl.campaign.ids.Terrain;
030import com.fs.starfarer.api.impl.campaign.procgen.Constellation.ConstellationType;
031import com.fs.starfarer.api.impl.campaign.procgen.ConstellationGen.SpringConnection;
032import com.fs.starfarer.api.impl.campaign.procgen.ConstellationGen.SpringSystem;
033import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator;
034import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity;
035import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.EntityLocation;
036import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.LocationType;
037import com.fs.starfarer.api.impl.campaign.terrain.BaseTiledTerrain.TileParams;
038import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
039import com.fs.starfarer.api.impl.campaign.terrain.NebulaTerrainPlugin;
040import com.fs.starfarer.api.impl.campaign.terrain.PulsarBeamTerrainPlugin;
041import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin;
042import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin.CoronaParams;
043import com.fs.starfarer.api.util.Misc;
044import com.fs.starfarer.api.util.WeightedRandomPicker;
045
046
047public class StarSystemGenerator {
048        
049        public static class CustomConstellationParams implements Cloneable {
050                public String name = null;
051                public String secondaryName = null;
052                public StarAge age = null;
053                public int minStars = 0;
054                public int maxStars = 0;
055                public int numStars = 0;
056                public boolean forceNebula = false;
057                public List<StarSystemType> systemTypes = new ArrayList<StarSystemType>();
058                public List<String> starTypes = new ArrayList<String>();
059                public Vector2f location = null;
060                
061                public CustomConstellationParams(StarAge age) {
062                        this.age = age;
063                }
064
065                @Override
066                public CustomConstellationParams clone() {
067                        try {
068                                return (CustomConstellationParams) super.clone();
069                        } catch (CloneNotSupportedException e) {
070                                return null;
071                        }
072                }
073                
074                
075        }
076        
077        
078        public static enum StarSystemType {
079                SINGLE,
080                BINARY_CLOSE,
081                BINARY_FAR,
082                TRINARY_2CLOSE,
083                TRINARY_1CLOSE_1FAR,
084                TRINARY_2FAR,
085                NEBULA,
086                DEEP_SPACE,
087                
088                @Deprecated DEEP_SPACE_GAS_GIANT,
089        }
090        
091        public static final float MIN_STAR_DIST = 2000f;
092        public static final float MAX_STAR_DIST = 2000f;
093        
094        public static final float TILT_MIN = -45f;
095        public static final float TILT_MAX = 45f;
096        public static final float PITCH_MIN = -15f;
097        public static final float PITCH_MAX = 45f;
098        
099        public static final float MAX_ORBIT_RADIUS = 20000;
100        public static final float FAR_MAX_ORBIT_RADIUS = 5000;
101        
102        public static final float LAGRANGE_OFFSET = 60f;
103        
104        public static final float BASE_INCR = 800f;
105        public static final float BASE_INCR_MOON = 200f;
106        
107        public static final float STARTING_RADIUS_STAR_BASE = 750f;
108        public static final float STARTING_RADIUS_STAR_RANGE = 500f;
109        
110        public static final float STARTING_RADIUS_MOON_BASE = 300f;
111        public static final float STARTING_RADIUS_MOON_RANGE = 100f;
112        
113        //public static final float MOON_RADIUS_MULT = 0.75f;
114        public static final float MOON_RADIUS_MAX_FRACTION_OF_PARENT = 0.33f;
115        public static final float MOON_RADIUS_MIN_FRACTION_OF_NORMAL = 0.2f;
116        public static final float MOON_RADIUS_MAX_FRACTION_OF_NORMAL = 0.75f;
117        public static final float MIN_MOON_RADIUS = 60f;
118        //public static final float MAX_MOON_RADIUS = 100;
119        
120        
121        public static final String TAG_FIRST_ORBIT_ONLY = "first_orbit_only";
122        public static final String TAG_GIANT_MOON = "around_giant_at_any_offset";
123        public static final String TAG_LAGRANGE_ONLY = "lagrange_only";
124        public static final String TAG_NOT_IN_NEBULA = "not_in_nebula";
125        public static final String TAG_REQUIRES_NEBULA = "requires_nebula";
126        
127        public static final String TAG_NOT_NEBULA_UNLESS_MOON = "not_NEBULA_unless_moon";
128        
129        public static final String CAT_HAB5 = "cat_hab5";
130        public static final String CAT_HAB4 = "cat_hab4";
131        public static final String CAT_HAB3 = "cat_hab3";
132        public static final String CAT_HAB2 = "cat_hab2";
133        public static final String CAT_HAB1 = "cat_hab1";
134        
135        public static final String CAT_NOTHING = "cat_nothing";
136        public static final String CAT_GIANT = "cat_giant";
137        
138        public static final String COL_LAGRANGE = "lagrange";
139        public static final String COL_IN_ASTEROIDS = "in_asteroids";
140        public static final String COL_IS_MOON = "is_moon";
141        public static final String COL_BINARY = "binary";
142        public static final String COL_TRINARY = "trinary";
143        
144        public static final String NEBULA_DEFAULT = "nebula";
145        public static final String NEBULA_AMBER = "nebula_amber";
146        public static final String NEBULA_BLUE = "nebula_blue";
147        public static final String NEBULA_NONE = "no_nebula";
148        
149        
150        public static Map<StarAge, String> nebulaTypes = new LinkedHashMap<StarAge, String>();
151        public static Map<String, WeightedRandomPicker<String>> backgroundsByNebulaType = new LinkedHashMap<String, WeightedRandomPicker<String>>(); 
152        
153        
154        public static List<TerrainGenPlugin> terrainPlugins = new ArrayList<TerrainGenPlugin>();
155        public static void addTerrainGenPlugin(TerrainGenPlugin plugin) {
156                terrainPlugins.add(0, plugin);
157        }
158        public static void removeTerrainGenPlugin(TerrainGenPlugin plugin) {
159                terrainPlugins.remove(plugin);
160        }
161        public static Random random = new Random();
162        
163        static {
164                terrainPlugins.add(new RingGenPlugin());
165                terrainPlugins.add(new AsteroidBeltGenPlugin());
166                terrainPlugins.add(new MagFieldGenPlugin());
167                
168                terrainPlugins.add(new NebulaSmallGenPlugin());
169                terrainPlugins.add(new AsteroidFieldGenPlugin());
170                
171                terrainPlugins.add(new AccretionDiskGenPlugin());
172                
173                nebulaTypes.put(StarAge.YOUNG, NEBULA_BLUE);
174                nebulaTypes.put(StarAge.AVERAGE, NEBULA_DEFAULT);
175                nebulaTypes.put(StarAge.OLD, NEBULA_AMBER);
176                
177                nebulaTypes.put(StarAge.ANY, NEBULA_DEFAULT);
178                
179                updateBackgroundPickers();
180        }
181        
182        public static void updateBackgroundPickers()
183        {
184                WeightedRandomPicker<String> picker;
185                picker = new WeightedRandomPicker<String>(random);
186                picker.add("graphics/backgrounds/background2.jpg", 10);
187                picker.add("graphics/backgrounds/background4.jpg", 10);
188                backgroundsByNebulaType.put(NEBULA_NONE, picker);
189                
190                picker = new WeightedRandomPicker<String>(random);
191                picker.add("graphics/backgrounds/background5.jpg", 10);
192                backgroundsByNebulaType.put(NEBULA_BLUE, picker);
193                
194                picker = new WeightedRandomPicker<String>(random);
195                picker.add("graphics/backgrounds/background6.jpg", 10);
196                backgroundsByNebulaType.put(NEBULA_AMBER, picker);
197                
198                picker = new WeightedRandomPicker<String>(random);
199                picker.add("graphics/backgrounds/background1.jpg", 10);
200                picker.add("graphics/backgrounds/background2.jpg", 10);
201                backgroundsByNebulaType.put(NEBULA_DEFAULT, picker);
202        }
203        
204        public static boolean DEBUG = Global.getSettings().isDevMode();
205        
206        public static TerrainGenPlugin pickTerrainGenPlugin(TerrainGenDataSpec terrainData, GenContext context) {
207                for (TerrainGenPlugin plugin : terrainPlugins) {
208                        if (plugin.wantsToHandle(terrainData, context)) return plugin;
209                }
210                return null;
211        }
212
213        
214        public static class GeneratedPlanet {
215                public SectorEntityToken parent;
216                public PlanetAPI planet;
217                public float orbitDays;
218                public float orbitRadius;
219                public float orbitAngle;
220                public boolean isMoon;
221                public GeneratedPlanet(SectorEntityToken parent, PlanetAPI planet, boolean isMoon, float orbitDays, float orbitRadius, float orbitAngle) {
222                        this.parent = parent;
223                        this.planet = planet;
224                        this.isMoon = isMoon;
225                        this.orbitDays = orbitDays;
226                        this.orbitRadius = orbitRadius;
227                        this.orbitAngle = orbitAngle;
228                }
229        }
230
231        public static enum LagrangePointType {
232//              L1,
233//              L2,
234//              L3,
235                L4,
236                L5,
237        }
238        
239        
240        public static class LagrangePoint {
241                public GeneratedPlanet parent;
242                public LagrangePointType type;
243                public LagrangePoint(GeneratedPlanet parent, LagrangePointType type) {
244                        this.parent = parent;
245                        this.type = type;
246                }
247        }
248        
249        
250        public static class GenResult {
251                public float orbitalWidth;
252                public boolean onlyIncrementByWidth = false;
253                public List<SectorEntityToken> entities = new ArrayList<SectorEntityToken>();
254                public GenContext context;
255                
256                public GenResult() {
257                        
258                }
259        }
260        
261        public static class GenContext {
262                public StarSystemGenerator gen;
263                public List<GeneratedPlanet> generatedPlanets = new ArrayList<GeneratedPlanet>();
264                
265                public Set<String> excludeCategories = new LinkedHashSet<String>();
266                
267                //public NamePick parentNamePick = null;
268                public StarSystemAPI system;
269                public SectorEntityToken center;
270                public StarGenDataSpec starData;
271                public PlanetAPI parent;
272                public int orbitIndex = -1;
273                
274                public int startingOrbitIndex = 0;
275                public String age;
276                
277                public float currentRadius;
278                public String parentCategory;
279                public int parentOrbitIndex;
280                public float parentRadiusOverride = -1;
281                
282                public GeneratedPlanet lagrangeParent = null;
283                public LagrangePointType lagrangePointType = null;
284                
285                public List<String> multipliers = new ArrayList<String>();
286                public float maxOrbitRadius;
287                
288                public Map<Object, Object> customData = new LinkedHashMap<Object, Object>();
289                
290                public GenContext(StarSystemGenerator gen, StarSystemAPI system, SectorEntityToken center,
291                                StarGenDataSpec starData, PlanetAPI parent,
292                                //NamePick parentNamePick, 
293                                int orbitIndex,
294                                String age, float currentRadius, float maxOrbitRadius, String parentCategory, int parentOrbitIndex) {
295                        super();
296                        //this.parentNamePick = parentNamePick;
297                        this.maxOrbitRadius = maxOrbitRadius;
298                        this.gen = gen;
299                        this.system = system;
300                        this.center = center;
301                        this.starData = starData;
302                        this.parent = parent;
303                        this.startingOrbitIndex = orbitIndex;
304                        this.orbitIndex = 0;
305                        this.age = age;
306                        this.currentRadius = currentRadius;
307                        this.parentCategory = parentCategory;
308                        this.parentOrbitIndex = parentOrbitIndex;
309                }
310                
311        }
312        
313        //private static long index = 0;
314        
315        protected StarAge constellationAge;
316        
317        protected StarSystemType systemType = StarSystemType.SINGLE;
318        protected StarAge starAge;
319        protected SectorAPI sector;
320        protected StarSystemAPI system;
321        protected LocationAPI hyper;
322        
323        protected PlanetAPI star;
324        protected PlanetAPI secondary;
325        protected PlanetAPI tertiary;
326        
327        protected SectorEntityToken systemCenter;
328        protected float centerRadius = 0f;
329        protected AgeGenDataSpec constellationAgeData;
330        protected AgeGenDataSpec starAgeData;
331        protected StarGenDataSpec starData;
332        protected String nebulaType;
333        protected String backgroundName;
334//      protected NamePick constellationName;
335//      protected NamePick primaryName;
336//      protected NamePick secondaryName;
337//      protected NamePick tertiaryName;
338        
339        protected Map<SectorEntityToken, PlanetAPI> lagrangeParentMap = new LinkedHashMap<SectorEntityToken, PlanetAPI>();
340        protected Map<SectorEntityToken, List<SectorEntityToken>> allNameableEntitiesAdded = new LinkedHashMap<SectorEntityToken, List<SectorEntityToken>>();
341        protected CustomConstellationParams params;
342
343        
344        public StarSystemGenerator(CustomConstellationParams params) {
345                this.params = params;
346                this.constellationAge = params.age;
347                
348                if (this.constellationAge == StarAge.ANY) {
349                        WeightedRandomPicker<StarAge> picker = new WeightedRandomPicker<StarAge>(random);
350                        picker.add(StarAge.AVERAGE);
351                        picker.add(StarAge.OLD);
352                        picker.add(StarAge.YOUNG);
353                        this.constellationAge = picker.pick();
354                }
355                
356                constellationAgeData = (AgeGenDataSpec) Global.getSettings().getSpec(AgeGenDataSpec.class, constellationAge.name(), true);
357        }
358
359        
360        
361        
362        
363        public void pickNebulaAndBackground() {
364                boolean hasNebula = constellationAgeData.getProbNebula() > random.nextFloat();
365                if (params != null && params.forceNebula) hasNebula = true;
366                
367//              hasNebula = true;
368//              hasNebula = false;
369                
370                nebulaType = NEBULA_NONE;
371                if (hasNebula) {
372                        nebulaType = nebulaTypes.get(constellationAge);
373                }
374                
375                WeightedRandomPicker<String> bgPicker = backgroundsByNebulaType.get(nebulaType);
376                backgroundName = bgPicker.pick();
377        }
378        
379        
380        
381        
382        public Constellation generate() {
383
384//              if (true) {
385//                      params = new CustomConstellationParams(StarAge.OLD);
386//                      params.numStars = 1;
387//                      params.starTypes.add(StarTypes.YELLOW);
388//                      params.systemTypes.add(StarSystemType.SINGLE);
389//                      
390//                      this.constellationAge = params.age;
391//                      constellationAgeData = (AgeGenDataSpec) Global.getSettings().getSpec(AgeGenDataSpec.class, constellationAge.name(), true);
392//              }
393                
394                //random = new Random(12312312312L);
395                
396                //DEBUG = false;
397                
398                lagrangeParentMap = new LinkedHashMap<SectorEntityToken, PlanetAPI>();
399                allNameableEntitiesAdded = new LinkedHashMap<SectorEntityToken, List<SectorEntityToken>>();
400                
401                SectorAPI sector = Global.getSector();
402                Vector2f loc = new Vector2f();
403                if (params != null && params.location != null) {
404                        loc = new Vector2f(params.location);
405                } else {
406                        loc = new Vector2f(sector.getPlayerFleet().getLocation());
407                        loc.x = (int) loc.x;
408                        loc.y = (int) loc.y;
409                }
410                
411                
412                pickNebulaAndBackground();
413                
414                List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>();
415                int stars = (int) Math.round(getNormalRandom(1, 7));
416                if (params != null && params.numStars > 0) {
417                        stars = params.numStars;
418                } else if (params != null && params.minStars > 0 && params.maxStars > 0) {
419                        stars = (int) Math.round(getNormalRandom(params.minStars, params.maxStars));
420                }
421                
422//              constellationName = ProcgenUsedNames.pickName(NameGenData.TAG_CONSTELLATION, null);
423//              ProcgenUsedNames.notifyUsed(constellationName.nameWithRomanSuffixIfAny);
424//              Global.getSettings().greekLetterReset();
425                
426                for (int i = 0; i < stars; i++) {
427                        generateSystem(new Vector2f(0, 0));
428                        if (system != null) {
429                                systems.add(system);
430                        }
431                }
432                
433                
434                SpringSystem springs = ConstellationGen.doConstellationLayout(systems, random, loc);
435                Global.getSector().getHyperspace().updateAllOrbits();
436                
437                HyperspaceTerrainPlugin plugin = (HyperspaceTerrainPlugin) Misc.getHyperspaceTerrain().getPlugin();
438                NebulaEditor editor = new NebulaEditor(plugin);
439                
440                float minRadius = plugin.getTileSize() * 2f;
441                for (StarSystemAPI curr : systems) {
442                        float radius = curr.getMaxRadiusInHyperspace();
443                        editor.clearArc(curr.getLocation().x, curr.getLocation().y, 0, radius + minRadius * 0.5f, 0, 360f);
444                        editor.clearArc(curr.getLocation().x, curr.getLocation().y, 0, radius + minRadius, 0, 360f, 0.25f);
445                }
446                
447                for (SpringConnection conn : springs.connections) {
448                        if (!conn.pull) continue;
449                        float r1 = ((StarSystemAPI)conn.from.custom).getMaxRadiusInHyperspace();
450                        float r2 = ((StarSystemAPI)conn.to.custom).getMaxRadiusInHyperspace();
451                        float dist = Misc.getDistance(conn.from.loc, conn.to.loc);
452                        
453                        float radius = Math.max(0, dist * 0.67f - r1 - r2);
454                        
455//                      float x = (conn.from.loc.x + conn.to.loc.x) * 0.5f;
456//                      float y = (conn.from.loc.y + conn.to.loc.y) * 0.5f;
457//                      editor.clearArc(x, y, 0, radius + minRadius * 0.5f, 0, 360f);
458//                      editor.clearArc(x, y, 0, radius + minRadius, 0, 360f, 0.25f);
459                        
460                        Vector2f diff = Vector2f.sub(conn.to.loc, conn.from.loc, new Vector2f());
461                        float x = conn.from.loc.x + diff.x * 0.33f;
462                        float y = conn.from.loc.y + diff.y * 0.33f;
463                        editor.clearArc(x, y, 0, radius + minRadius * 1f, 0, 360f);
464                        editor.clearArc(x, y, 0, radius + minRadius * 2f, 0, 360f, 0.25f);
465                        
466                        x = conn.from.loc.x + diff.x * 0.67f;
467                        y = conn.from.loc.y + diff.y * 0.67f;
468                        editor.clearArc(x, y, 0, radius + minRadius * 1f, 0, 360f);
469                        editor.clearArc(x, y, 0, radius + minRadius * 2f, 0, 360f, 0.25f);
470                }
471                
472                ConstellationType type = ConstellationType.NORMAL;
473                if (!NEBULA_NONE.equals(nebulaType)) {
474                        type = ConstellationType.NEBULA;
475                }
476                
477                Constellation c = new Constellation(type, constellationAge);
478                //c.setType(ConstellationType.NORMAL); // for now; too many end up being called "Nebula" otherwise
479                c.getSystems().addAll(systems);
480                c.setLagrangeParentMap(lagrangeParentMap);
481                c.setAllEntitiesAdded(allNameableEntitiesAdded);
482
483                
484//              SalvageEntityGeneratorOld seg = new SalvageEntityGeneratorOld(c);
485//              seg.addSalvageableEntities();
486                
487
488                NameAssigner namer = new NameAssigner(c);
489                namer.setSpecialNamesProbability(0.37f);
490                namer.assignNames(params.name, params.secondaryName);
491                
492                for (SectorEntityToken entity : allNameableEntitiesAdded.keySet()) {
493                        if (entity instanceof PlanetAPI && entity.getMarket() != null) {
494                                entity.getMarket().setName(entity.getName());
495                        }
496                }
497                
498                //if (systems.size() > 1) {
499                        for (StarSystemAPI system : systems) {
500                                system.setConstellation(c);
501                        }
502                //}
503                
504                return c;
505        }
506        
507        
508        public void generateSystem(Vector2f loc) {
509                //index++;
510                
511                systemType = pickSystemType(constellationAge);
512                
513//              if (systemType == StarSystemType.NEBULA) {
514//                      System.out.println("wfwefwe1231232");
515//              }
516                
517                String uuid = Misc.genUID();
518                
519//              String id = "system_" + index;
520//              String name = "System " + index;
521                
522                String id = "system_" + uuid;
523                String name = "System " + uuid;
524                
525//              String base = constellationName.nameWithRomanSuffixIfAny;
526//              if (constellationName.secondaryWithRomanSuffixIfAny != null) {
527//                      base = constellationName.secondaryWithRomanSuffixIfAny;
528//              }
529//              String name = Global.getSettings().getNextGreekLetter(constellationName) + " " + base;
530//              String id = name.toLowerCase();
531                
532                
533                //if (systemType == StarSystemType.NEBULA) name += " Nebula";
534                
535                if (!initSystem(name, loc)) {
536                        cleanup();
537                        return;
538                }
539                
540                star = null;
541                secondary = null;
542                tertiary = null;
543                systemCenter = null;
544                
545                
546                
547                if (!addStars(id)) {
548                        cleanup();
549                        return;
550                }
551                
552//              if (systemType == StarSystemType.NEBULA) {
553//                      if (star.getSpec().isBlackHole()) {
554//                              System.out.println("wefwefew");
555//                      }
556//              }
557                
558                updateAgeAfterPickingStar();
559                
560                float binaryPad = 1500f;
561                
562                float maxOrbitRadius = MAX_ORBIT_RADIUS;
563                if (systemType == StarSystemType.BINARY_FAR || 
564                                systemType == StarSystemType.TRINARY_1CLOSE_1FAR ||
565                                systemType == StarSystemType.TRINARY_2FAR) {
566                        maxOrbitRadius -= FAR_MAX_ORBIT_RADIUS + binaryPad;
567                }
568                GenResult result = addPlanetsAndTerrain(MAX_ORBIT_RADIUS);
569                //addJumpPoints(result);
570                float primaryOrbitalRadius = star.getRadius();
571                if (result != null) {
572                        primaryOrbitalRadius = result.orbitalWidth * 0.5f;
573                }
574                
575                // add far stars, if needed
576                float orbitAngle = random.nextFloat() * 360f;
577                float baseOrbitRadius = primaryOrbitalRadius + binaryPad;
578                float orbitDays = baseOrbitRadius / (3f + random.nextFloat() * 2f);
579                if (systemType == StarSystemType.BINARY_FAR && secondary != null) {
580                        addFarStar(secondary, orbitAngle, baseOrbitRadius, orbitDays);
581                } else if (systemType == StarSystemType.TRINARY_1CLOSE_1FAR && tertiary != null) {
582                        addFarStar(tertiary, orbitAngle, baseOrbitRadius, orbitDays);
583                } else if (systemType == StarSystemType.TRINARY_2FAR) {
584                        addFarStar(secondary, orbitAngle, baseOrbitRadius, orbitDays);
585                        addFarStar(tertiary, orbitAngle + 60f + 180f * random.nextFloat(), baseOrbitRadius, orbitDays);
586                }
587                
588
589                if (systemType == StarSystemType.NEBULA) {
590                        star.setSkipForJumpPointAutoGen(true);
591                }
592                
593                addJumpPoints(result, false);
594                
595                if (systemType == StarSystemType.NEBULA) {
596                        system.removeEntity(star);
597                        StarCoronaTerrainPlugin coronaPlugin = Misc.getCoronaFor(star);
598                        if (coronaPlugin != null) {
599                                system.removeEntity(coronaPlugin.getEntity());
600                        }
601                        system.setStar(null);
602                        system.initNonStarCenter();
603                        for (SectorEntityToken entity : system.getAllEntities()) {
604                                if (entity.getOrbitFocus() == star ||
605                                        entity.getOrbitFocus() == system.getCenter()) {
606                                        entity.setOrbit(null);
607                                }
608                        }
609                        system.getCenter().addTag(Tags.AMBIENT_LS);
610                }
611                
612                system.autogenerateHyperspaceJumpPoints(true, false);
613                
614                if (systemType == StarSystemType.NEBULA) {
615                        //system.addEntity(star);
616                        system.setStar(star);
617                        //system.removeEntity(system.getCenter());
618                        //system.setCenter(null);
619                }
620                
621                addStableLocations();
622                
623                addSystemwideNebula();
624        }
625        
626        
627        protected void addFarStar(PlanetAPI farStar, float orbitAngle, float baseOrbitRadius, float orbitPeriod) {
628                float min = 0;
629                float max = 2;
630                int numOrbits = (int) Math.round(getNormalRandom(min, max));
631                GenResult resultFar = null;
632                if (numOrbits > 0) {
633                        float currentRadius = farStar.getRadius() + STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
634
635                        StarGenDataSpec farData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, farStar.getSpec().getPlanetType(), false);
636                        StarAge farAge = farData.getAge();
637                        if (farAge == StarAge.ANY) {
638                                farAge = constellationAge;
639                        }
640                        
641                        //GenContext context = new GenContext(this, system, star, starData, 
642                        GenContext context = new GenContext(this, system, farStar, farData, 
643                                                                null, 0, farAge.name(), currentRadius, FAR_MAX_ORBIT_RADIUS, null, -1);
644                        
645                        
646                        resultFar = addOrbitingEntities(context, numOrbits, false, true, false, true);
647                        resultFar.context = context;
648                }
649                
650                if (resultFar != null) {
651                        baseOrbitRadius += resultFar.orbitalWidth * 0.5f;
652                }
653                
654                SectorEntityToken center = star;
655                if (systemType == StarSystemType.TRINARY_1CLOSE_1FAR) {
656                        center = systemCenter;
657                }
658                farStar.setCircularOrbit(center, orbitAngle, baseOrbitRadius, orbitPeriod);
659                
660                if (resultFar != null) {
661                        addJumpPoints(resultFar, true);
662                }
663        }
664        
665        
666        
667        protected StarSystemType pickSystemType(StarAge constellationAge) {
668//              if (true) {
669//                      return StarSystemType.BINARY_CLOSE;
670//              }
671                
672                if (params != null && !params.systemTypes.isEmpty()) {
673                        return params.systemTypes.remove(0);
674                }
675                
676                
677                WeightedRandomPicker<StarSystemType> picker = new WeightedRandomPicker<StarSystemType>(random);
678                for (StarSystemType type : EnumSet.allOf(StarSystemType.class)) {
679                        if (type == StarSystemType.DEEP_SPACE || type == StarSystemType.DEEP_SPACE_GAS_GIANT) continue;
680                        
681                        Object test = Global.getSettings().getSpec(LocationGenDataSpec.class, type.name(), true);
682                        if (test == null) continue;
683                        LocationGenDataSpec data = (LocationGenDataSpec) test;
684                        
685                        boolean nebulaStatusOk = NEBULA_NONE.equals(nebulaType) || !data.hasTag(TAG_NOT_IN_NEBULA);
686                        nebulaStatusOk &= !NEBULA_NONE.equals(nebulaType) || !data.hasTag(TAG_REQUIRES_NEBULA);
687
688                        if (!nebulaStatusOk) continue;
689                        
690                        float freq = 0f;
691                        switch (constellationAge) {
692                        case AVERAGE:
693                                freq = data.getFreqAVERAGE();
694                                break;
695                        case OLD:
696                                freq = data.getFreqOLD();
697                                break;
698                        case YOUNG:
699                                freq = data.getFreqYOUNG();
700                                break;
701                        }
702                        picker.add(type, freq);
703                }
704                
705                return picker.pick();
706                
707                //return StarSystemType.TRINARY_1CLOSE_1FAR;
708                //return StarSystemType.TRINARY_2FAR;
709                //return StarSystemType.TRINARY_2CLOSE;
710                //return StarSystemType.BINARY_CLOSE;
711                //return StarSystemType.BINARY_FAR;
712                //return StarSystemType.NORMAL;
713        }
714        
715        
716        protected boolean addStars(String id) {
717                if (systemType == StarSystemType.BINARY_CLOSE || 
718                                systemType == StarSystemType.TRINARY_1CLOSE_1FAR ||
719                                systemType == StarSystemType.TRINARY_2CLOSE) {
720                        system.initNonStarCenter();
721                        systemCenter = system.getCenter();
722                }
723                
724//              if (systemType == StarSystemType.NEBULA) {
725//                      if (system.getId().equals("system 7f6")) {
726//                              System.out.println("wefwefew");
727//                      }
728//              }
729                
730                PlanetSpecAPI starSpec = pickStar(constellationAge);
731                if (starSpec == null) return false;
732                
733                starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, starSpec.getPlanetType(), false);
734                float radius = getRadius(starData.getMinRadius(), starData.getMaxRadius());
735                
736                float corona = radius * (starData.getCoronaMult() + starData.getCoronaVar() * (random.nextFloat() - 0.5f));
737                if (corona < starData.getCoronaMin()) corona = starData.getCoronaMin();
738                
739                //corona += 2000f;
740
741                star = system.initStar(id, // unique id for this star 
742                                                                                    starSpec.getPlanetType(),  // id in planets.json
743                                                                                    radius,               // radius (in pixels at default zoom)
744                                                                                    corona,  // corona radius, from star edge 
745                                                                                    starData.getSolarWind(),
746                                                                                    (float) (starData.getMinFlare() + (starData.getMaxFlare() - starData.getMinFlare()) * random.nextFloat()),
747                                                                                    starData.getCrLossMult()
748                                                                                        );
749                
750                if (systemType == StarSystemType.NEBULA) {
751                        star.addTag(Tags.AMBIENT_LS);
752                }
753                
754                if (systemCenter == null) {
755                        systemCenter = star;
756                        centerRadius = star.getRadius();
757                }
758                
759                
760                // create and switch aronud stars so that the primary is always the largest
761                if (systemType == StarSystemType.BINARY_CLOSE) {
762                        secondary = addRandomStar(id + "_b", system.getBaseName() + " B");
763                        if (secondary == null) return false;
764                        switchPrimaryAndSecondaryIfNeeded(true);
765                } else if (systemType == StarSystemType.BINARY_FAR) {
766                        secondary = addRandomStar(id + "_b", system.getBaseName() + " B");
767                        if (secondary == null) return false;
768                        switchPrimaryAndSecondaryIfNeeded(true);
769                        
770                        centerRadius = star.getRadius();
771                        
772                        secondary.setLightColorOverrideIfStar(pickLightColorForStar(secondary));
773                } else if (systemType == StarSystemType.TRINARY_2CLOSE ||
774                                   systemType == StarSystemType.TRINARY_1CLOSE_1FAR ||
775                                   systemType == StarSystemType.TRINARY_2FAR) {
776                        secondary = addRandomStar(id + "_b", system.getBaseName() + " B");
777                        if (secondary == null) return false;
778                        switchPrimaryAndSecondaryIfNeeded(true);
779                        
780                        tertiary = addRandomStar(id + "_c", system.getBaseName() + " C");
781                        if (tertiary == null) return false;
782                        switchPrimaryAndTertiaryIfNeeded(true);
783                        
784                        if (systemType == StarSystemType.TRINARY_1CLOSE_1FAR) {
785                                tertiary.setLightColorOverrideIfStar(pickLightColorForStar(tertiary));
786                        } else if (systemType == StarSystemType.TRINARY_2FAR) {
787                                secondary.setLightColorOverrideIfStar(pickLightColorForStar(secondary));
788                                tertiary.setLightColorOverrideIfStar(pickLightColorForStar(tertiary));
789                        }
790                }
791                
792                // make close stars orbit common center
793                if (systemType == StarSystemType.BINARY_CLOSE || systemType == StarSystemType.TRINARY_1CLOSE_1FAR) {
794                        float dist = STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
795                        
796                        float r1 = star.getRadius();
797                        float r2 = secondary.getRadius();
798                        if (star.getSpec().getPlanetType().equals("black_hole")) r1 *= 5f;
799                        if (secondary.getSpec().getPlanetType().equals("black_hole")) r2 *= 5f;
800                        
801                        float totalRadius = r1 + r2;
802                        dist += totalRadius;
803                        
804                        float orbitPrimary = dist * r2 / totalRadius;
805                        float orbitSecondary = dist * r1 / totalRadius;
806                        
807                        centerRadius = Math.max(orbitPrimary + star.getRadius(), orbitSecondary + secondary.getRadius()); 
808                        
809                        float anglePrimary = random.nextFloat() * 360f;
810                        float orbitDays = dist / (30f + random.nextFloat() * 50f); 
811                        
812                        star.setCircularOrbit(system.getCenter(), anglePrimary, orbitPrimary, orbitDays);
813                        secondary.setCircularOrbit(system.getCenter(), anglePrimary + 180f, orbitSecondary, orbitDays);
814                        
815//                      if (systemType == StarSystemType.TRINARY_1CLOSE_1FAR && tertiary != null) {
816//                              tertiary.setCircularOrbit(system.getCenter(), 
817//                                              tertiary.getCircularOrbitAngle(),
818//                                              tertiary.getCircularOrbitRadius(),
819//                                              tertiary.getCircularOrbitPeriod());
820//                      }
821                } else if (systemType == StarSystemType.TRINARY_2CLOSE) {
822                        float dist = STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
823                        dist += star.getRadius();
824                        
825                        float anglePrimary = random.nextFloat() * 360f;
826                        float orbitDays = dist / (20f + random.nextFloat() * 80f);
827                        
828                        // smaller dist for primary/secondary so that their gravity wells get generated first
829                        // and are closer to the center
830                        star.setCircularOrbit(system.getCenter(), anglePrimary, dist - 10, orbitDays);
831                        secondary.setCircularOrbit(system.getCenter(), anglePrimary + 120f, dist - 5, orbitDays);
832                        tertiary.setCircularOrbit(system.getCenter(), anglePrimary + 240f, dist, orbitDays);
833                        
834                        centerRadius = dist + star.getRadius();
835                } else {
836                        star.getLocation().set(0, 0);
837                }
838                
839
840                if (star != null) {
841                        starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, star.getSpec().getPlanetType(), false);
842                }
843//              if (systemCenter instanceof PlanetAPI) {
844//                      centerRadius = star.getRadius();
845//              }
846                
847
848                setDefaultLightColorBasedOnStars();
849                
850                
851                if (star != null) {
852                        ArrayList<SectorEntityToken> list = new ArrayList<SectorEntityToken>();
853                        list.add(star);
854                        allNameableEntitiesAdded.put(star, list);
855                        system.setStar(star);
856                }
857                if (secondary != null) {
858                        ArrayList<SectorEntityToken> list = new ArrayList<SectorEntityToken>();
859                        list.add(secondary);
860                        allNameableEntitiesAdded.put(secondary, list);
861                        system.setSecondary(secondary);
862                }
863                if (tertiary != null){
864                        ArrayList<SectorEntityToken> list = new ArrayList<SectorEntityToken>();
865                        list.add(tertiary);
866                        allNameableEntitiesAdded.put(tertiary, list);
867                        system.setTertiary(tertiary);
868                }
869                
870                setBlackHoleIfBlackHole(star);
871                setBlackHoleIfBlackHole(secondary);
872                setBlackHoleIfBlackHole(tertiary);
873                
874                setPulsarIfNeutron(star);
875                setPulsarIfNeutron(secondary);
876                setPulsarIfNeutron(tertiary);
877                
878                return true;
879        }
880        
881        protected void setBlackHoleIfBlackHole(PlanetAPI star) {
882                if (star == null) return;
883                
884                if (star.getSpec().getPlanetType().equals("black_hole")) {
885                        StarCoronaTerrainPlugin coronaPlugin = Misc.getCoronaFor(star);
886                        if (coronaPlugin != null) {
887                                system.removeEntity(coronaPlugin.getEntity());
888                        }
889                        
890                        StarGenDataSpec starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, star.getSpec().getPlanetType(), false);
891                        float corona = star.getRadius() * (starData.getCoronaMult() + starData.getCoronaVar() * (random.nextFloat() - 0.5f));
892                        if (corona < starData.getCoronaMin()) corona = starData.getCoronaMin();
893                        
894                        SectorEntityToken eventHorizon = system.addTerrain(Terrain.EVENT_HORIZON, 
895                                        new CoronaParams(star.getRadius() + corona, (star.getRadius() + corona) / 2f,
896                                                                        star, starData.getSolarWind(), 
897                                                                        (float) (starData.getMinFlare() + (starData.getMaxFlare() - starData.getMinFlare()) * random.nextFloat()),
898                                                                        starData.getCrLossMult()));
899                        eventHorizon.setCircularOrbit(star, 0, 0, 100);
900                }
901        }
902        
903        protected void setPulsarIfNeutron(PlanetAPI star) {
904                if (star == null) return;
905                
906                if (star.getSpec().getPlanetType().equals("star_neutron")) {
907                        StarCoronaTerrainPlugin coronaPlugin = Misc.getCoronaFor(star);
908                        if (coronaPlugin != null) {
909                                system.removeEntity(coronaPlugin.getEntity());
910                        }
911                        
912                        system.addCorona(star, 
913                                                        300, // radius
914                                                        3, // wind
915                                                        0, // flares
916                                                        3); // cr loss
917                        
918                        
919                        StarGenDataSpec starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, star.getSpec().getPlanetType(), false);
920                        float corona = star.getRadius() * (starData.getCoronaMult() + starData.getCoronaVar() * (random.nextFloat() - 0.5f));
921                        if (corona < starData.getCoronaMin()) corona = starData.getCoronaMin();
922                        
923                        SectorEntityToken eventHorizon = system.addTerrain(Terrain.PULSAR_BEAM, 
924                                        new CoronaParams(star.getRadius() + corona, (star.getRadius() + corona) / 2f,
925                                                        star, starData.getSolarWind(), 
926                                                        (float) (starData.getMinFlare() + (starData.getMaxFlare() - starData.getMinFlare()) * random.nextFloat()),
927                                                        starData.getCrLossMult()));
928                        eventHorizon.setCircularOrbit(star, 0, 0, 100);
929                }
930        }
931        
932        protected void switchPrimaryAndSecondaryIfNeeded(boolean sizeDownSmaller) {
933                if (star == null || secondary == null) return;
934                
935                if (star.getRadius() < secondary.getRadius()) {
936                        star = secondary;
937                        secondary = system.getStar();
938                        system.setStar(star);
939                        
940                        String temp = star.getName();
941                        star.setName(secondary.getName());
942                        secondary.setName(temp);
943                        
944                        temp = star.getId();
945                        star.setId(secondary.getId());
946                        secondary.setId(temp);
947                        
948                        if (sizeDownSmaller) {
949                                secondary.setRadius(Math.min(secondary.getRadius(), star.getRadius() * 0.67f));
950                        }
951                        
952                        if (secondary == systemCenter) {
953                                systemCenter = star;
954                                centerRadius = systemCenter.getRadius();
955                        }
956                }
957        }
958        
959        protected void switchPrimaryAndTertiaryIfNeeded(boolean sizeDownSmaller) {
960                if (star == null || tertiary == null) return;
961                
962                if (star.getRadius() < tertiary.getRadius()) {
963                        star = tertiary;
964                        tertiary = system.getStar();
965                        system.setStar(star);
966                        
967                        String temp = star.getName();
968                        star.setName(tertiary.getName());
969                        tertiary.setName(temp);
970                        
971                        temp = star.getId();
972                        star.setId(tertiary.getId());
973                        tertiary.setId(temp);
974                        
975                        if (sizeDownSmaller) {
976                                tertiary.setRadius(Math.min(tertiary.getRadius(), star.getRadius() * 0.67f));
977                        }
978                        
979                        if (tertiary == systemCenter) {
980                                systemCenter = star;
981                                centerRadius = systemCenter.getRadius();
982                        }
983                }
984        }
985        
986        
987        
988        protected void addStableLocations() {
989                int min = 1;
990                int max = 3;
991                
992//              int planets = system.getPlanets().size(); // includes stars
993//              max = Math.min(max, planets/2);
994//              if (min > max) min = max;
995                
996                //int num = (int) Math.round(getNormalRandom(min, max));
997                int num = random.nextInt(max + 1 - min) + min;
998                
999                if (num == min && random.nextFloat() < 0.25f) {
1000                        num = 0;
1001                }
1002                
1003                addStableLocations(system, num);
1004        }
1005        public static void addStableLocations(StarSystemAPI system, int num) {
1006                for (int i = 0; i < num; i++) {
1007                        LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
1008                        weights.put(LocationType.STAR_ORBIT, 10f);
1009                        weights.put(LocationType.OUTER_SYSTEM, 10f);
1010                        weights.put(LocationType.L_POINT, 10f);
1011                        weights.put(LocationType.IN_SMALL_NEBULA, 2f);
1012                        WeightedRandomPicker<EntityLocation> locs = BaseThemeGenerator.getLocations(random, system, null, 100f, weights);
1013                        EntityLocation loc = locs.pick();
1014                        
1015                        AddedEntity added = BaseThemeGenerator.addNonSalvageEntity(system, loc, Entities.STABLE_LOCATION, Factions.NEUTRAL);
1016                        //if (DEBUG && added != null) System.out.println("    Added stable location");
1017                        
1018                        if (added != null) {
1019                                BaseThemeGenerator.convertOrbitPointingDown(added.entity);
1020                        }
1021                }
1022        }
1023        
1024        
1025        protected void addJumpPoints(GenResult result, boolean farStarMode) {
1026                
1027                //float outerRadius = star.getRadius() + STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
1028                float outerRadius = centerRadius + STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
1029                if (result != null) {
1030                        outerRadius = result.orbitalWidth / 2f + 500f;
1031                }
1032                
1033                if (farStarMode) {
1034                        //if (result.context.orbitIndex < 0) {
1035                        if (result.context.orbitIndex < 0 && random.nextFloat() < 0.5f) {
1036                                return;
1037                        }
1038                        SectorEntityToken farStar = result.context.center;
1039                        String name = "Omega Jump-point";
1040                        if (result.context.center == tertiary) {
1041                                name = "Omicron Jump-point";
1042                        }
1043                        JumpPointAPI point = Global.getFactory().createJumpPoint(null, name);
1044                        point.setStandardWormholeToHyperspaceVisual();
1045                        float orbitDays = outerRadius / (15f + random.nextFloat() * 5f);
1046                        //point.setCircularOrbit(star, random.nextFloat() * 360f, outerRadius, orbitDays);
1047                        point.setCircularOrbit(farStar, random.nextFloat() * 360f, outerRadius, orbitDays);
1048                        system.addEntity(point);
1049                        
1050                        return;
1051                }
1052                
1053                
1054                //JumpPointAPI point = Global.getFactory().createJumpPoint(null, system.getBaseName() + " Fringe");
1055                JumpPointAPI point = Global.getFactory().createJumpPoint(null, "Fringe Jump-point");
1056                point.setStandardWormholeToHyperspaceVisual();
1057                float orbitDays = outerRadius / (15f + random.nextFloat() * 5f);
1058                //point.setCircularOrbit(star, random.nextFloat() * 360f, outerRadius, orbitDays);
1059                point.setCircularOrbit(systemCenter, random.nextFloat() * 360f, outerRadius, orbitDays);
1060                system.addEntity(point);
1061
1062                
1063                // to make sure that "is this location clear" calculations below always work
1064                system.updateAllOrbits();
1065                
1066                
1067                if (result != null) {
1068                        float halfway = outerRadius * 0.5f;
1069                        
1070                        WeightedRandomPicker<LagrangePoint> inner = new WeightedRandomPicker<LagrangePoint>(random);
1071                        WeightedRandomPicker<LagrangePoint> outer = new WeightedRandomPicker<LagrangePoint>(random);
1072                        
1073                        int total = 0;
1074                        for (GeneratedPlanet planet : result.context.generatedPlanets) {
1075                                if (planet.isMoon) continue;
1076                                if (planet.planet.getOrbit() == null || planet.planet.getOrbit().getFocus() == null) continue;
1077                                total++;
1078                                
1079                                for (LagrangePointType type : EnumSet.of(LagrangePointType.L4, LagrangePointType.L5)) {
1080                                        float orbitRadius = planet.orbitRadius;
1081                                        float angleOffset = -LAGRANGE_OFFSET * 0.5f;
1082                                        if (type == LagrangePointType.L5) angleOffset = LAGRANGE_OFFSET * 0.5f;
1083                                        float angle = planet.orbitAngle + angleOffset;
1084                                        Vector2f location = Misc.getUnitVectorAtDegreeAngle(angle + angleOffset);
1085                                        location.scale(orbitRadius);
1086                                        Vector2f.add(location, planet.planet.getOrbit().getFocus().getLocation(), location);
1087                                        
1088                                        boolean clear = true;
1089                                        for (PlanetAPI curr : system.getPlanets()) {
1090                                                float dist = Misc.getDistance(curr.getLocation(), location);
1091                                                //System.out.println(type.name() + ": " + location + ", " + "Dist: " + dist + " (to " + curr.getName() + " at " + curr.getLocation() + ")");
1092                                                if (dist < 500) {
1093                                                        clear = false;
1094                                                        break;
1095                                                }
1096                                        }
1097                                        if (clear) {
1098                                                if (planet.orbitRadius < halfway || planet.orbitRadius < 5000f) {
1099                                                        inner.add(new LagrangePoint(planet, type), 10f);
1100                                                } else {
1101                                                        outer.add(new LagrangePoint(planet, type), 10f);
1102                                                }
1103                                        }
1104                                }
1105                        }
1106                        
1107                        
1108                        if (outerRadius > 2000f + 5000f * random.nextFloat()) {
1109                                boolean addedOne = false;
1110                                if (!inner.isEmpty()) {
1111                                        LagrangePoint p = inner.pick();
1112                                        //addJumpPoint(p, p.parent.planet.getName() + " Jump-point");
1113                                        String name = "Inner System Jump-point";
1114                                        if (systemType == StarSystemType.NEBULA) name = "Inner Jump-point";
1115                                        addJumpPoint(p, name);
1116                                        addedOne = true;
1117                                }
1118                                
1119                                
1120                                if (!outer.isEmpty() && (random.nextFloat() < outer.getItems().size() * 0.2f || !addedOne)) {
1121                                        LagrangePoint p = outer.pick();
1122                                        String name = "Outer System Jump-point";
1123                                        if (systemType == StarSystemType.NEBULA) name = "Outer Jump-point";
1124                                        addJumpPoint(p, name);
1125                                        //addJumpPoint(p, "Outer System Jump-point");
1126                                        addedOne = true;
1127                                }
1128                                
1129//                              while (!inner.isEmpty()) {
1130//                                      LagrangePoint p = inner.pickAndRemove();
1131//                                      //addJumpPoint(p, p.parent.planet.getName() + " Jump-point");
1132//                                      addJumpPoint(p, system.getBaseName() + " Inner System Jump-point");
1133//                              }
1134//                              while (!outer.isEmpty()) {
1135//                                      LagrangePoint p = outer.pickAndRemove();
1136//                                      //addJumpPoint(p, p.parent.planet.getName() + " Jump-point");
1137//                                      addJumpPoint(p, system.getBaseName() + " Outer System Jump-point");
1138//                              }
1139                        }
1140                }
1141                
1142                //system.autogenerateHyperspaceJumpPoints(true, false);
1143        }
1144        
1145        protected void addJumpPoint(LagrangePoint p, String name) {
1146                float orbitRadius = p.parent.orbitRadius;
1147                float orbitDays = p.parent.orbitDays;
1148                float angleOffset = -LAGRANGE_OFFSET * 0.5f;
1149                if (p.type == LagrangePointType.L5) angleOffset = LAGRANGE_OFFSET * 0.5f;
1150                float angle = p.parent.orbitAngle + angleOffset;
1151
1152                SectorEntityToken focus = p.parent.planet.getOrbitFocus();
1153                if (focus == null) focus = systemCenter;
1154                
1155                JumpPointAPI point = Global.getFactory().createJumpPoint(null, name);
1156                point.setStandardWormholeToHyperspaceVisual();
1157                if (!p.parent.planet.isGasGiant()) {
1158                        point.setRelatedPlanet(p.parent.planet);
1159                }
1160                //point.setCircularOrbit(star, angle + angleOffset, orbitRadius, orbitDays);
1161                //point.setCircularOrbit(systemCenter, angle + angleOffset, orbitRadius, orbitDays);
1162                point.setCircularOrbit(focus, angle + angleOffset, orbitRadius, orbitDays);
1163                system.addEntity(point);
1164        }
1165        
1166        public static float addOrbitingEntities(StarSystemAPI system, SectorEntityToken parentStar, StarAge age,
1167                        int min, int max, float startingRadius,
1168                        int nameOffset, boolean withSpecialNames) {
1169                return addOrbitingEntities(system, parentStar, age, min, max, startingRadius, nameOffset, withSpecialNames, true);
1170        }
1171        
1172        public static float addOrbitingEntities(StarSystemAPI system, SectorEntityToken parentStar, StarAge age,
1173                                                                                   int min, int max, float startingRadius,
1174                                                                                   int nameOffset, boolean withSpecialNames,
1175                                                                                   boolean allowHabitable) {
1176                CustomConstellationParams p = new CustomConstellationParams(age);
1177                p.forceNebula = true; // not sure why this is here; should avoid small nebula at lagrange points though (but is that desired?)
1178                
1179                StarSystemGenerator gen = new StarSystemGenerator(p);
1180                gen.system = system;
1181                gen.starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, system.getStar().getSpec().getPlanetType(), false);
1182                gen.starAge = age;
1183                gen.constellationAge = age;
1184                gen.starAgeData = (AgeGenDataSpec) Global.getSettings().getSpec(AgeGenDataSpec.class, age.name(), true);
1185                gen.star = system.getStar();
1186                
1187                gen.pickNebulaAndBackground();
1188                if (system.getType() != null) gen.systemType = system.getType();
1189                
1190                
1191                gen.systemCenter = system.getCenter();
1192                
1193                StarGenDataSpec starData = gen.starData;
1194                PlanetGenDataSpec planetData = null;
1195                PlanetAPI parentPlanet = null;
1196                if (parentStar instanceof PlanetAPI) {
1197                        PlanetAPI planet = (PlanetAPI) parentStar;
1198                        if (planet.isStar()) {
1199                                starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, planet.getSpec().getPlanetType(), false);
1200                        } else {
1201                                planetData = (PlanetGenDataSpec) Global.getSettings().getSpec(PlanetGenDataSpec.class, planet.getSpec().getPlanetType(), false);
1202                                parentPlanet = planet;
1203                        }
1204                }
1205                
1206                int parentOrbitIndex = -1;
1207                int startingOrbitIndex = 0;
1208                
1209                boolean addingAroundStar = parentPlanet == null;
1210                float r = 0;
1211                if (parentStar != null) {
1212                        r = parentStar.getRadius();
1213                }
1214
1215                float approximateExtraRadiusPerOrbit = 400f;
1216                if (addingAroundStar) {
1217                        parentOrbitIndex = -1;
1218                        startingOrbitIndex = (int) ((startingRadius  - r - STARTING_RADIUS_STAR_BASE - STARTING_RADIUS_STAR_RANGE * 0.5f) /
1219                                                             (BASE_INCR * 1.25f + approximateExtraRadiusPerOrbit));
1220                        
1221                        if (startingOrbitIndex < 0) startingOrbitIndex = 0;
1222                } else {
1223                        float dist = 0f;
1224                        if (parentPlanet.getOrbitFocus() != null) {
1225                                dist = Misc.getDistance(parentPlanet.getLocation(), parentPlanet.getOrbitFocus().getLocation());
1226                        }
1227                        parentOrbitIndex = (int) ((dist - r - STARTING_RADIUS_STAR_BASE - STARTING_RADIUS_STAR_RANGE * 0.5f) /
1228                                                           (BASE_INCR * 1.25f + approximateExtraRadiusPerOrbit));
1229                        startingOrbitIndex = (int) ((startingRadius - STARTING_RADIUS_MOON_BASE - STARTING_RADIUS_MOON_RANGE * 0.5f) /
1230                                                           (BASE_INCR_MOON * 1.25f));
1231                        
1232                        if (parentOrbitIndex < 0) parentOrbitIndex = 0;
1233                        if (startingOrbitIndex < 0) startingOrbitIndex = 0;
1234                }
1235                
1236                int num = (int) Math.round(getNormalRandom(min, max));
1237                
1238                GenContext context = new GenContext(gen, system, gen.systemCenter, starData, 
1239                                                        parentPlanet, startingOrbitIndex, age.name(), startingRadius, MAX_ORBIT_RADIUS,
1240                                                        planetData != null ? planetData.getCategory() : null, parentOrbitIndex);
1241                
1242                if (!allowHabitable) {
1243                        context.excludeCategories.add(CAT_HAB5);
1244                        context.excludeCategories.add(CAT_HAB4);
1245                        context.excludeCategories.add(CAT_HAB3);
1246                        context.excludeCategories.add(CAT_HAB2);
1247                }
1248                
1249                GenResult result = gen.addOrbitingEntities(context, num, false, addingAroundStar, false, false);
1250                
1251                
1252                Constellation c = new Constellation(ConstellationType.NORMAL, age);
1253                c.getSystems().add(system);
1254                c.setLagrangeParentMap(gen.lagrangeParentMap);
1255                c.setAllEntitiesAdded(gen.allNameableEntitiesAdded);
1256                c.setLeavePickedNameUnused(true);
1257                NameAssigner namer = new NameAssigner(c);
1258                if (withSpecialNames) {
1259                        namer.setSpecialNamesProbability(1f);
1260                } else {
1261                        namer.setSpecialNamesProbability(0f);
1262                }
1263                namer.setRenameSystem(false);
1264                namer.setStructuralNameOffset(nameOffset);
1265                namer.assignNames(null, null);
1266                
1267                for (SectorEntityToken entity : gen.allNameableEntitiesAdded.keySet()) {
1268                        if (entity instanceof PlanetAPI && entity.getMarket() != null) {
1269                                entity.getMarket().setName(entity.getName());
1270                        }
1271                }
1272                
1273                return result.orbitalWidth * 0.5f;
1274                
1275        }
1276        
1277        public static void addSystemwideNebula(StarSystemAPI system, StarAge age) {
1278                CustomConstellationParams p = new CustomConstellationParams(age);
1279                p.forceNebula = true;
1280                
1281                StarSystemGenerator gen = new StarSystemGenerator(p);
1282                gen.system = system;
1283                gen.starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, system.getStar().getSpec().getPlanetType(), false);
1284                gen.starAge = age;
1285                gen.constellationAge = age;
1286                gen.starAgeData = (AgeGenDataSpec) Global.getSettings().getSpec(AgeGenDataSpec.class, age.name(), true);
1287                gen.pickNebulaAndBackground();
1288                if (system.getType() != null) gen.systemType = system.getType();
1289                
1290                gen.addSystemwideNebula();
1291                
1292                system.setAge(age);
1293                system.setHasSystemwideNebula(true);
1294        }
1295        
1296        
1297        protected void addSystemwideNebula() {
1298                if (nebulaType.equals(NEBULA_NONE)) return;
1299                
1300                
1301                int w = 128;
1302                int h = 128;
1303                
1304                StringBuilder string = new StringBuilder();
1305                for (int y = h - 1; y >= 0; y--) {
1306                        for (int x = 0; x < w; x++) {
1307                                string.append("x");
1308                        }
1309                }
1310                SectorEntityToken nebula = system.addTerrain(Terrain.NEBULA, new TileParams(string.toString(),
1311                                                        w, h,
1312                                                        "terrain", nebulaType, 4, 4, null));
1313                nebula.getLocation().set(0, 0);
1314                
1315                NebulaTerrainPlugin nebulaPlugin = (NebulaTerrainPlugin)((CampaignTerrainAPI)nebula).getPlugin();
1316                NebulaEditor editor = new NebulaEditor(nebulaPlugin);
1317                
1318                editor.regenNoise();
1319                
1320                // good medium thickness: 0.6
1321                //editor.noisePrune(0.8f);
1322                
1323                // yes, star age here, despite using constellation age to determine if a nebula to all exists
1324                // basically: young star in old constellation will have lots of nebula, but of the constellation-age color
1325                editor.noisePrune(starAgeData.getNebulaDensity());
1326                //editor.noisePrune(0.75f);
1327                //editor.noisePrune(0.1f);
1328                
1329//              for (float f = 0.1f; f <= 0.9f; f += 0.05f) {
1330//                      editor.noisePrune(f);
1331//              }
1332                
1333                editor.regenNoise();
1334                
1335                if (systemType != StarSystemType.NEBULA) {
1336                        for (PlanetAPI planet : system.getPlanets()) {
1337                                
1338                                if (planet.getOrbit() != null && planet.getOrbit().getFocus() != null &&
1339                                                planet.getOrbit().getFocus().getOrbit() != null) {
1340                                        // this planet is orbiting something that's orbiting something
1341                                        // its motion will be relative to its parent moving
1342                                        // don't clear anything out for this planet
1343                                        continue;
1344                                }
1345                                
1346                                float clearThreshold = 0f; // clear everything by default
1347                                float clearInnerRadius = 0f;
1348                                float clearOuterRadius = 0f;
1349                                Vector2f clearLoc = null;
1350        
1351                                
1352                                if (!planet.isStar() && !planet.isGasGiant()) {
1353                                        clearThreshold = 1f - Math.min(0f, planet.getRadius() / 300f);
1354                                        if (clearThreshold > 0.5f) clearThreshold = 0.5f;
1355                                }
1356                                
1357                                Vector2f loc = planet.getLocation();
1358                                if (planet.getOrbit() != null && planet.getOrbit().getFocus() != null) {
1359                                        Vector2f focusLoc = planet.getOrbit().getFocus().getLocation();
1360                                        float dist = Misc.getDistance(planet.getOrbit().getFocus().getLocation(), loc);
1361                                        float width = planet.getRadius() * 4f + 100f;
1362                                        if (planet.isStar()) {
1363                                                StarCoronaTerrainPlugin corona = Misc.getCoronaFor(planet);
1364                                                if (corona != null) {
1365                                                        width = corona.getParams().bandWidthInEngine * 4f;
1366                                                }
1367                                                PulsarBeamTerrainPlugin pulsar = Misc.getPulsarFor(planet);
1368                                                if (pulsar != null) {
1369                                                        width = Math.max(width, pulsar.getParams().bandWidthInEngine * 0.5f);
1370                                                }
1371                                        }
1372                                        clearLoc = focusLoc;
1373                                        clearInnerRadius = dist - width / 2f;
1374                                        clearOuterRadius = dist + width / 2f;
1375                                } else if (planet.getOrbit() == null) {
1376                                        float width = planet.getRadius() * 4f + 100f;
1377                                        if (planet.isStar()) {
1378                                                StarCoronaTerrainPlugin corona = Misc.getCoronaFor(planet);
1379                                                if (corona != null) {
1380                                                        width = corona.getParams().bandWidthInEngine * 4f;
1381                                                }
1382                                                PulsarBeamTerrainPlugin pulsar = Misc.getPulsarFor(planet);
1383                                                if (pulsar != null) {
1384                                                        width = Math.max(width, pulsar.getParams().bandWidthInEngine * 0.5f);
1385                                                }
1386                                        }
1387                                        clearLoc = loc;
1388                                        clearInnerRadius = 0f;
1389                                        clearOuterRadius = width;
1390                                }
1391                                
1392                                if (clearLoc != null) {
1393                                        float min = nebulaPlugin.getTileSize() * 2f;
1394                                        if (clearOuterRadius - clearInnerRadius < min) {
1395                                                clearOuterRadius = clearInnerRadius + min;
1396                                        }
1397                                        editor.clearArc(clearLoc.x, clearLoc.y, clearInnerRadius, clearOuterRadius, 0, 360f, clearThreshold);
1398                                }
1399                        }
1400                }
1401
1402                // add a spiral going from the outside towards the star
1403                float angleOffset = random.nextFloat() * 360f;
1404                editor.clearArc(0f, 0f, 30000, 31000 + 1000f * random.nextFloat(), 
1405                                angleOffset + 0f, angleOffset + 360f * (2f + random.nextFloat() * 2f), 0.01f, 0f);
1406                
1407                // do some random arcs
1408                int numArcs = (int) (8f + 6f * random.nextFloat());
1409                //int numArcs = 11;
1410                
1411                for (int i = 0; i < numArcs; i++) {
1412                        //float dist = 4000f + 10000f * random.nextFloat();
1413                        float dist = 15000f + 15000f * random.nextFloat();
1414                        float angle = random.nextFloat() * 360f;
1415                        
1416                        Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle);
1417                        dir.scale(dist - (2000f + 8000f * random.nextFloat()));
1418                        
1419                        //float tileSize = nebulaPlugin.getTileSize();
1420                        //float width = tileSize * (2f + 4f * random.nextFloat());
1421                        float width = 800f * (1f + 2f * random.nextFloat());
1422                        
1423                        float clearThreshold = 0f + 0.5f * random.nextFloat();
1424                        //clearThreshold = 0f;
1425                        
1426                        editor.clearArc(dir.x, dir.y, dist - width/2f, dist + width/2f, 0, 360f, clearThreshold);
1427                }
1428        }
1429        
1430        
1431        protected GenResult addPlanetsAndTerrain(float maxOrbitRadius) {
1432                boolean hasOrbits = random.nextFloat() < starData.getProbOrbits();
1433                if (!hasOrbits) return null;
1434                
1435                float min = starData.getMinOrbits() + starAgeData.getMinExtraOrbits();
1436                float max = starData.getMaxOrbits() + starAgeData.getMaxExtraOrbits();
1437                int numOrbits = (int) Math.round(getNormalRandom(min, max));
1438                
1439                if (numOrbits <= 0) return null;
1440
1441                
1442                
1443                float currentRadius = centerRadius + STARTING_RADIUS_STAR_BASE + STARTING_RADIUS_STAR_RANGE * random.nextFloat();
1444
1445                //GenContext context = new GenContext(this, system, star, starData, 
1446                GenContext context = new GenContext(this, system, systemCenter, starData, 
1447                                                        null, 0, starAge.name(), currentRadius, maxOrbitRadius, null, -1);
1448                
1449                if (systemType == StarSystemType.BINARY_CLOSE || systemType == StarSystemType.TRINARY_1CLOSE_1FAR) {
1450                        context.multipliers.add(COL_BINARY);
1451                }
1452                if (systemType == StarSystemType.TRINARY_2CLOSE) {
1453                        context.multipliers.add(COL_TRINARY);
1454                }
1455                
1456                
1457                GenResult result = addOrbitingEntities(context, numOrbits, false, true, false, true);
1458                result.context = context;
1459                return result;
1460        }
1461                
1462        protected GenResult addOrbitingEntities(GenContext context, int numOrbits, boolean addingMoons, boolean addMoons, boolean parentIsMoon, boolean nothingOk) {
1463                
1464                if (DEBUG && context.starData != null) {
1465                        if (addingMoons && context.parent != null) {
1466                                System.out.println("  Adding " + numOrbits + " moon orbits around " + context.parent.getSpec().getPlanetType());
1467                        } else {
1468                                System.out.println("Adding " + numOrbits + " orbits around " + context.starData.getId());
1469                        }
1470                }
1471                
1472                float currentRadius = context.currentRadius;
1473                float lastIncrementExtra = 0f;
1474                
1475                String extraMult = null;
1476                if (parentIsMoon) extraMult = COL_IS_MOON;
1477
1478                int extra = 0;
1479                for (int i = 0; i < numOrbits; i++) {
1480                        
1481                        context.orbitIndex = i + context.startingOrbitIndex + extra;
1482                        //CategoryGenDataSpec categoryData = pickCategory(orbitIndex, i, starAge.name(), starType, context.parentCategory, null, nothingOk);
1483                        CategoryGenDataSpec categoryData = pickCategory(context, extraMult, nothingOk);
1484                        nothingOk = true; // only applies to first pick, after this, we'll have *something*
1485
1486//                      if (!addingMoons) {
1487//                              categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_giant", true);
1488//                              //categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_terrain_rings", true);
1489//                      }
1490//                      if (orbitIndex == 0 && addMoons) {
1491//                              categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_giant", true);
1492//                              //categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_hab4", true);
1493//                      }
1494//                      if (orbitIndex == 1 && addMoons) {
1495//                      if (!addingMoons) {
1496//                              categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_giant", true);
1497//                      }
1498//                      } else {
1499//                              categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_terrain_rings", true);
1500//                      }
1501                        //categoryData = (CategoryGenDataSpec) Global.getSettings().getSpec(CategoryGenDataSpec.class, "cat_terrain_rings", true);
1502                        
1503                        GenResult result = null;
1504                        float incrMult = 1f;
1505
1506                        if (categoryData != null && !CAT_NOTHING.equals(categoryData.getCategory())) {
1507                                //WeightedRandomPicker<EntityGenDataSpec> picker = getPickerForCategory(categoryData, orbitIndex, i, starAge.name(), starType, context.parentCategory, null);
1508                                WeightedRandomPicker<EntityGenDataSpec> picker = getPickerForCategory(categoryData, context, extraMult);
1509                                if (DEBUG) {
1510                                        picker.print("  Picking from category " + categoryData.getCategory() + 
1511                                                        ", orbit index " + (context.parent != null ? context.parentOrbitIndex : context.orbitIndex));
1512                                }
1513                                EntityGenDataSpec entityData = picker.pick();
1514                                if (DEBUG) {
1515                                        if (entityData == null) {
1516                                                System.out.println("  Nothing to pick");
1517                                                System.out.println();
1518                                        } else {
1519                                                System.out.println("  Picked: " + entityData.getId());
1520                                                System.out.println();
1521                                        }
1522                                }
1523                                
1524                                context.currentRadius = currentRadius;
1525                                //context.orbitIndex = i;
1526                                
1527                                if (entityData instanceof PlanetGenDataSpec) {
1528                                        PlanetGenDataSpec planetData = (PlanetGenDataSpec) entityData;
1529                                        result = addPlanet(context, planetData, addingMoons, addMoons);
1530
1531                                } else if (entityData instanceof TerrainGenDataSpec) {
1532                                        TerrainGenDataSpec terrainData = (TerrainGenDataSpec) entityData;
1533                                        result = addTerrain(context, terrainData);
1534                                }
1535                                
1536                                if (result != null) {
1537                                        //List<SectorEntityToken> combined = new ArrayList<SectorEntityToken>(result.entities);
1538                                        for (SectorEntityToken curr : result.entities) {
1539                                                if (context.lagrangeParent != null && !result.entities.isEmpty()) {
1540                                                        lagrangeParentMap.put(curr, context.lagrangeParent.planet);
1541                                                }
1542                                                allNameableEntitiesAdded.put(curr, result.entities);
1543                                        }
1544                                } else {
1545                                        incrMult = 0.5f;
1546                                }
1547                        } else {
1548                                incrMult = 0.5f;
1549                        }
1550                        
1551                        float baseIncr = BASE_INCR;
1552                        if (addingMoons) {
1553                                baseIncr = BASE_INCR_MOON;
1554                        }
1555                        baseIncr *= incrMult;
1556                        
1557                        float increment = baseIncr + baseIncr * 0.5f * random.nextFloat();
1558                        if (result != null) {
1559                                if (result.orbitalWidth > 1000) extra++;
1560                                //increment = Math.max(increment, result.orbitalWidth + baseIncr * 0.5f);
1561                                //increment += result.orbitalWidth;
1562                                increment = Math.max(increment + Math.min(result.orbitalWidth, 300f),
1563                                                                         result.orbitalWidth + increment * 0.5f);
1564                                if (result.onlyIncrementByWidth) {
1565                                        increment = result.orbitalWidth;
1566                                }
1567                                lastIncrementExtra = Math.max(increment * 0.1f, increment - result.orbitalWidth);
1568                        } else {
1569                                lastIncrementExtra = increment;
1570                        }
1571                        currentRadius += increment;
1572                        
1573                        if (currentRadius >= context.maxOrbitRadius) {
1574                                break;
1575                        }
1576                }
1577                
1578                GenResult result = new GenResult();
1579                result.onlyIncrementByWidth = false;
1580                result.orbitalWidth = (currentRadius - lastIncrementExtra) * 2f;
1581                return result;
1582        }
1583        
1584        protected GenResult addTerrain(GenContext context, TerrainGenDataSpec terrainData) {
1585                TerrainGenPlugin plugin = pickTerrainGenPlugin(terrainData, context);
1586                if (plugin == null ) return null;
1587                GenResult result = plugin.generate(terrainData, context);
1588                
1589                
1590                return result;
1591        }
1592        
1593        
1594        public GenResult addPlanet(GenContext context, PlanetGenDataSpec planetData, boolean isMoon, boolean addMoons) {
1595                //float orbitRadius = (orbitIndex + 1f) * 1500 + star.getRadius(); 
1596                float radius = getRadius(planetData.getMinRadius(), planetData.getMaxRadius());
1597                if (isMoon) {// || context.lagrangeParent != null) {
1598                        //radius *= MOON_RADIUS_MULT;
1599                        float mult = MOON_RADIUS_MIN_FRACTION_OF_NORMAL + 
1600                        random.nextFloat() * (MOON_RADIUS_MAX_FRACTION_OF_NORMAL - MOON_RADIUS_MIN_FRACTION_OF_NORMAL);
1601                        radius *= mult;
1602                        if (radius < MIN_MOON_RADIUS) {
1603                                radius = MIN_MOON_RADIUS;
1604                        }
1605                        float parentRadius = 100000f;
1606                        if (context.parent != null || context.lagrangeParent != null) {
1607                                PlanetAPI parent = context.parent;
1608                                if (context.lagrangeParent != null) {
1609                                        parent = context.lagrangeParent.planet;
1610                                }
1611                                parentRadius = parent.getRadius();
1612                        }
1613                        if (context.parentRadiusOverride > 0) {
1614                                parentRadius = context.parentRadiusOverride;
1615                        }
1616                        
1617                        if (parentRadius > MIN_MOON_RADIUS / MOON_RADIUS_MAX_FRACTION_OF_PARENT) {
1618                                float max = parentRadius * MOON_RADIUS_MAX_FRACTION_OF_PARENT;
1619                                if (radius > max) {
1620                                        radius = max;
1621                                }
1622                        }
1623                }
1624                
1625                float orbitRadius = context.currentRadius + radius;
1626                float orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
1627                
1628                //String planetId = system.getId() + "_planet_" + context.center.getId() + "_" + context.orbitIndex;
1629                String planetId = context.center.getId() + ":planet_" + context.orbitIndex;
1630                String planetName = "Planet " + context.orbitIndex;
1631                if (context.parent != null) {
1632                        planetId = system.getId() + "_moon_" + context.center.getId() + "_" + context.parent.getId() + "_" + context.orbitIndex;
1633                        planetName = context.parent.getName() + " moon " + context.orbitIndex;
1634                }
1635                String planetType = planetData.getId();
1636                SectorEntityToken parent = context.center;
1637                if (context.parent != null) parent = context.parent;
1638                
1639                float angle = random.nextFloat() * 360f;
1640                
1641                // if adding a moon to a largange point planet, then parentCategory == null
1642                // will fail and it'll orbit the parent rather than stay at the point
1643                if (context.parentCategory == null) {
1644                        if (context.lagrangeParent != null && context.lagrangePointType != null) {
1645                                orbitRadius = context.lagrangeParent.orbitRadius;
1646                                orbitDays = context.lagrangeParent.orbitDays;
1647                                float angleOffset = -LAGRANGE_OFFSET;
1648                                if (context.lagrangePointType == LagrangePointType.L5) angleOffset = LAGRANGE_OFFSET;
1649                                angle = context.lagrangeParent.orbitAngle + angleOffset;
1650                                planetName += " " + context.lagrangePointType.name();
1651                                planetId += "_" + context.lagrangePointType.name();
1652                        }
1653                }
1654                
1655                PlanetAPI planet = system.addPlanet(planetId, parent, planetName, planetType, angle, radius, orbitRadius, orbitDays);
1656                if (planet.isGasGiant()) {
1657                        if (systemType == StarSystemType.NEBULA) {
1658                                planet.setAutogenJumpPointNameInHyper(system.getBaseName() + ", " + planetName + " Gravity Well");
1659                        }
1660                }
1661
1662                float radiusWithMoons = planet.getRadius();
1663                if (addMoons) {
1664                        boolean hasOrbits = random.nextFloat() < planetData.getProbOrbits();
1665                        float min = planetData.getMinOrbits();
1666                        float max = planetData.getMaxOrbits();
1667//                      double r = random.nextFloat();
1668//                      r *= r;
1669//                      int numOrbits = (int) (min + Math.round((max - min) * r));
1670                        int numOrbits = (int) Math.round(getNormalRandom(min, max));
1671                        
1672//                      hasOrbits = true;
1673//                      numOrbits = 2;
1674                        
1675//                      if (numOrbits == 3) {
1676//                              System.out.println("efwefwefew");
1677//                      }
1678                        
1679                        if (hasOrbits && numOrbits > 0) {
1680//                              if (!planet.isGasGiant()) {
1681//                                      System.out.println("sdfwefew " + star.getId());
1682//                              }
1683                                float startingRadius = planet.getRadius() + STARTING_RADIUS_MOON_BASE + STARTING_RADIUS_MOON_RANGE * random.nextFloat();
1684                                GenContext moonContext = new GenContext(this, context.system, context.center, context.starData, planet, 0, starAge.name(),
1685                                                                                                                startingRadius, context.maxOrbitRadius, planetData.getCategory(), context.orbitIndex);
1686                                moonContext.excludeCategories.addAll(context.excludeCategories);
1687                                moonContext.multipliers.addAll(context.multipliers);
1688                                // add moons etc
1689                                GenResult moonResult = addOrbitingEntities(moonContext, numOrbits, true, false, false, false);
1690
1691                                context.generatedPlanets.addAll(moonContext.generatedPlanets);
1692                                
1693                                // move the parent planet out so that there's room for everythnig that was added
1694                                radius = moonResult.orbitalWidth * 0.5f;
1695                                orbitRadius = context.currentRadius + radius;
1696                                orbitDays = orbitRadius / (20f + random.nextFloat() * 5f);
1697                                
1698                                radiusWithMoons = radius;
1699                                
1700                                planet.setOrbit(Global.getFactory().createCircularOrbit(context.center, angle, orbitRadius, orbitDays));
1701                        }
1702                } else if (isMoon) {
1703                        float startingRadius = planet.getRadius() + STARTING_RADIUS_MOON_BASE + STARTING_RADIUS_MOON_RANGE * random.nextFloat();
1704                        GenContext moonContext = new GenContext(this, context.system, context.center, context.starData, planet, 0, starAge.name(),
1705                                                                                                        startingRadius, context.maxOrbitRadius, planetData.getCategory(), context.orbitIndex);
1706                        moonContext.excludeCategories.addAll(context.excludeCategories);
1707                        moonContext.multipliers.addAll(context.multipliers);
1708                        GenResult moonResult = addOrbitingEntities(moonContext, 1, true, false, true, true);
1709                        context.generatedPlanets.addAll(moonContext.generatedPlanets);
1710                        
1711                }
1712                
1713                
1714                Color color = getColor(planetData.getMinColor(), planetData.getMaxColor());
1715                //System.out.println("Setting color: " + color);
1716                planet.getSpec().setPlanetColor(color);
1717                if (planet.getSpec().getAtmosphereThickness() > 0) {
1718                        Color atmosphereColor = Misc.interpolateColor(planet.getSpec().getAtmosphereColor(), color, 0.25f);
1719                        atmosphereColor = Misc.setAlpha(atmosphereColor, planet.getSpec().getAtmosphereColor().getAlpha());
1720                        planet.getSpec().setAtmosphereColor(atmosphereColor);
1721                        
1722                        if (planet.getSpec().getCloudTexture() != null) {
1723                                Color cloudColor = Misc.interpolateColor(planet.getSpec().getCloudColor(), color, 0.25f);
1724                                cloudColor = Misc.setAlpha(cloudColor, planet.getSpec().getCloudColor().getAlpha());
1725                                planet.getSpec().setAtmosphereColor(atmosphereColor);
1726                        }
1727                }
1728                
1729                float tilt = planet.getSpec().getTilt();
1730                float pitch = planet.getSpec().getPitch();
1731                
1732//              "tilt" # left-right (>0 tilts to the left)
1733//              "pitch" # towards-away from the viewer (>0 pitches towards)
1734                float sign = (float) Math.signum(random.nextFloat() - 0.5f);
1735                double r = random.nextFloat();
1736                //r *= r;
1737                if (sign > 0) {
1738                        tilt += r * TILT_MAX;
1739                } else {
1740                        tilt += r * TILT_MIN;
1741                }
1742                
1743                sign = (float) Math.signum(random.nextFloat() - 0.5f);
1744                r = random.nextFloat();
1745                //r *= r;
1746                if (sign > 0) {
1747                        pitch += r * PITCH_MAX;
1748                } else {
1749                        tilt += r * PITCH_MIN;
1750                }
1751                planet.getSpec().setTilt(tilt);
1752                planet.getSpec().setPitch(pitch);
1753                
1754                
1755                if (context.orbitIndex == 0 && context.parent == null && context.orbitIndex < context.starData.getHabZoneStart() &&
1756                                orbitRadius < 2500f + context.starData.getHabZoneStart() * 200f) {
1757                                //&& radiusWithMoons <= planet.getRadius() + 500f) {
1758                        if (planet.getSpec().getAtmosphereThickness() > 0) {
1759                                WeightedRandomPicker<String> glowPicker = new WeightedRandomPicker<String>(random);
1760                                glowPicker.add("banded", 10f);
1761                                glowPicker.add("aurorae", 10f);
1762                                
1763                                String glow = glowPicker.pick();
1764                                
1765                                planet.getSpec().setGlowTexture(Global.getSettings().getSpriteName("hab_glows", glow));
1766                                //system.getLightColor();
1767                                if (context.center instanceof PlanetAPI) {
1768                                        planet.getSpec().setGlowColor(((PlanetAPI)context.center).getSpec().getCoronaColor());
1769                                }
1770                                planet.getSpec().setUseReverseLightForGlow(true);
1771                                planet.getSpec().setAtmosphereThickness(0.5f);
1772                                planet.getSpec().setCloudRotation(planet.getSpec().getCloudRotation() * (-1f - 2f * random.nextFloat()));
1773                                
1774                                if (planet.isGasGiant()) {// && radiusWithMoons <= planet.getRadius() + 500f) {
1775                                        system.addCorona(planet, Terrain.CORONA_AKA_MAINYU,
1776                                                        300f + 200f * random.nextFloat(), // radius outside planet
1777                                                        5f, // burn level of "wind"
1778                                                        0f, // flare probability
1779                                                        1f // CR loss mult while in it
1780                                                        );
1781                                }
1782                        }
1783                }
1784                
1785                planet.applySpecChanges();
1786                
1787                PlanetConditionGenerator.generateConditionsForPlanet(context, planet);
1788                
1789                
1790                GeneratedPlanet generatedPlanetData = new GeneratedPlanet(parent, planet, isMoon, orbitDays, orbitRadius, angle);
1791                context.generatedPlanets.add(generatedPlanetData);
1792                
1793                // need to add this here because planet might have been moved after adding moons
1794                //if (context.parentCategory == null && context.lagrangeParent == null && planet.isGasGiant()) {
1795                if (!isMoon && context.lagrangeParent == null) {// && planet.isGasGiant()) {
1796                        addStuffAtLagrangePoints(context, generatedPlanetData);
1797                }
1798                
1799                GenResult result = new GenResult();
1800                result.orbitalWidth = radius * 2f;
1801                result.onlyIncrementByWidth = false;
1802                result.entities.add(planet);
1803                return result;
1804        }
1805        
1806        protected void addStuffAtLagrangePoints(GenContext context, GeneratedPlanet planet) {
1807                float radius = planet.planet.getRadius();
1808                float probability = radius / 500f;
1809                if (radius < 150f) probability = 0f;
1810                if (planet.planet.isGasGiant()) probability = 1f;
1811                if (random.nextFloat() > probability) {
1812                        return;
1813                }
1814                
1815                // still a high chance to end up w/ nothing if cat_nothing is picked
1816                
1817//              int orbitIndex = context.orbitIndex;
1818//              String starType = context.star.getTypeId();
1819//              if (context.parentOrbitIndex >= 0) {
1820//                      orbitIndex = context.parentOrbitIndex;
1821//              }
1822                
1823                Set<LagrangePointType> points = EnumSet.of(LagrangePointType.L4, LagrangePointType.L5);
1824                for (LagrangePointType point : points) {
1825                        //CategoryGenDataSpec categoryData = pickCategory(orbitIndex, 0, starAge.name(), starType, context.parentCategory, COL_LAGRANGE, true);
1826                        CategoryGenDataSpec categoryData = pickCategory(context, COL_LAGRANGE, true);
1827                        if (categoryData != null && !CAT_NOTHING.equals(categoryData.getCategory())) {
1828                                //WeightedRandomPicker<EntityGenDataSpec> picker = getPickerForCategory(categoryData, orbitIndex, 0, starAge.name(), starType, context.parentCategory, COL_LAGRANGE);
1829                                WeightedRandomPicker<EntityGenDataSpec> picker = getPickerForCategory(categoryData, context, COL_LAGRANGE);
1830                                if (DEBUG) {
1831                                        picker.print("  Picking from category " + categoryData.getCategory() + 
1832                                                        ", orbit index " + (context.parent != null ? context.parentOrbitIndex : context.orbitIndex + ", for lagrange point"));
1833                                }
1834                                EntityGenDataSpec entityData = picker.pick();
1835                                if (DEBUG) {
1836                                        if (entityData == null) {
1837                                                System.out.println("  Nothing to pick");
1838                                                System.out.println();
1839                                        } else {
1840                                                System.out.println("  Picked: " + entityData.getId());
1841                                                System.out.println();
1842                                        }
1843                                }
1844                                
1845                                context.lagrangeParent = planet;
1846                                context.lagrangePointType = point;
1847                                
1848                                GenResult result = null;
1849                                if (entityData instanceof PlanetGenDataSpec) {
1850                                        PlanetGenDataSpec planetData = (PlanetGenDataSpec) entityData;
1851                                        result = addPlanet(context, planetData, true, true);
1852                                } else if (entityData instanceof TerrainGenDataSpec) {
1853                                        TerrainGenDataSpec terrainData = (TerrainGenDataSpec) entityData;
1854                                        result = addTerrain(context, terrainData);
1855                                }
1856                                
1857                                if (result != null) {
1858                                        for (SectorEntityToken curr : result.entities) {
1859                                                if (context.lagrangeParent != null && !result.entities.isEmpty()) {
1860                                                        lagrangeParentMap.put(curr, context.lagrangeParent.planet);
1861                                                }
1862                                                allNameableEntitiesAdded.put(curr, result.entities);
1863                                        }
1864                                }
1865                        }
1866                }
1867                context.lagrangeParent = null;
1868                context.lagrangePointType = null;
1869        }
1870
1871        
1872        
1873        //protected CategoryGenDataSpec pickCategory(int orbitIndex, int fromParentOrbitIndex, String age, String starType, String parentCategory, String extraMult, boolean nothingOk) {
1874        public CategoryGenDataSpec pickCategory(GenContext context, String extraMult, boolean nothingOk) {
1875//              int orbitIndex = context.orbitIndex;
1876//              if (context.parentOrbitIndex >= 0) {
1877//                      orbitIndex = context.parentOrbitIndex;
1878//              }
1879//              int fromParentOrbitIndex = context.orbitIndex;
1880                String age = context.age;
1881                //String starType = context.star.getTypeId();
1882                String starType = star != null ? star.getTypeId() : null;
1883                if (context.center instanceof PlanetAPI) {
1884                        PlanetAPI star = (PlanetAPI) context.center;
1885                        if (star.isStar()) starType = star.getTypeId();
1886                }
1887                
1888                String parentCategory = context.parentCategory;
1889                
1890                WeightedRandomPicker<CategoryGenDataSpec> picker = new WeightedRandomPicker<CategoryGenDataSpec>(random);
1891                Collection<CategoryGenDataSpec> categoryDataSpecs = Global.getSettings().getAllSpecs(CategoryGenDataSpec.class);
1892                for (CategoryGenDataSpec categoryData : categoryDataSpecs) {
1893                        if (context.excludeCategories.contains(categoryData.getCategory())) continue;
1894                        
1895                        boolean catNothing = categoryData.getCategory().equals(CAT_NOTHING);
1896                        if (!nothingOk && catNothing) continue;
1897//                      if (categoryData.getCategory().equals("cat_terrain_rings")) {
1898//                              System.out.println("sdfkwefewfe");
1899//                      }
1900                        float weight = categoryData.getFrequency();
1901                        if (age != null) weight *= categoryData.getMultiplier(age);
1902                        if (starType != null) weight *= categoryData.getMultiplier(starType);
1903                        if (parentCategory != null) weight *= categoryData.getMultiplier(parentCategory);
1904                        for (String col : context.multipliers) {
1905                                weight *= categoryData.getMultiplier(col);
1906                        }
1907                        if (extraMult != null) weight *= categoryData.getMultiplier(extraMult);
1908                        
1909                        //if (weight > 0 && (catNothing || !isCategoryEmpty(categoryData, orbitIndex, fromParentOrbitIndex, age, starType, parentCategory, extraMult))) {
1910                        if (weight > 0 && (catNothing || !isCategoryEmpty(categoryData, context, extraMult, nothingOk))) {
1911                                picker.add(categoryData, weight); 
1912                        }
1913                }
1914                
1915                if (DEBUG) {
1916                        boolean withParent = context.parent != null;
1917                        int orbitIndex = context.orbitIndex;
1918                        String parentType = "";
1919                        if (withParent) {
1920                                parentType = context.parent.getSpec().getPlanetType();
1921                                orbitIndex = context.parentOrbitIndex;
1922                        }
1923                        
1924//                      float offset = orbitIndex;
1925//                      float minIndex = context.starData.getHabZoneStart() + planetData.getHabOffsetMin() + offset;
1926//                      float maxIndex = context.starData.getHabZoneStart() + planetData.getHabOffsetMax() + offset;
1927                        //boolean inRightRange = orbitIndex >= minIndex && orbitIndex <= maxIndex;
1928                        int habDiff = orbitIndex - (int) context.starData.getHabZoneStart();
1929                        if (withParent) {
1930                                picker.print("  Picking category for moon of " + parentType + 
1931                                                         ", orbit from star: " + orbitIndex + " (" + habDiff + ")" +  ", extra: " + extraMult);
1932                        } else {
1933                                picker.print("  Picking category for entity orbiting star " + starType + 
1934                                                        ", orbit from star: " + orbitIndex + " (" + habDiff + ")" +  ", extra: " + extraMult);
1935                        }
1936                }
1937                
1938                CategoryGenDataSpec pick = picker.pick();
1939                if (DEBUG) {
1940                        System.out.println("  Picked: " + pick.getCategory());
1941                        System.out.println();
1942                }
1943                
1944                return pick;
1945        }
1946
1947        public boolean isCategoryEmpty(CategoryGenDataSpec categoryData, GenContext context, String extraMult, boolean nothingOk) {
1948                return getPickerForCategory(categoryData, context, extraMult, nothingOk).isEmpty();
1949        }
1950        
1951        protected float getHabOffset(EntityGenDataSpec data) {
1952                if (starAge == StarAge.YOUNG) {
1953                        return data.getHabOffsetYOUNG();
1954                }
1955                if (starAge == StarAge.AVERAGE) {
1956                        return data.getHabOffsetAVERAGE();
1957                }
1958                if (starAge == StarAge.OLD) {
1959                        return data.getHabOffsetOLD();
1960                }
1961                return 0f;
1962        }
1963        
1964//      protected WeightedRandomPicker<EntityGenDataSpec> getPickerForCategory(CategoryGenDataSpec categoryData, 
1965//                      int orbitIndex, int fromParentOrbitIndex, String age, String starType, String parentCategory, String extraMult) {
1966        protected WeightedRandomPicker<EntityGenDataSpec> getPickerForCategory(CategoryGenDataSpec categoryData, 
1967                        GenContext context, String extraMult) {
1968                return getPickerForCategory(categoryData, context, extraMult, true);
1969        }
1970        protected WeightedRandomPicker<EntityGenDataSpec> getPickerForCategory(CategoryGenDataSpec categoryData, 
1971                                                                                                                                                   GenContext context, String extraMult, boolean nothingOk) {
1972                int orbitIndex = context.orbitIndex;
1973                if (context.parentOrbitIndex >= 0) {
1974                        orbitIndex = context.parentOrbitIndex;
1975                }
1976                int fromParentOrbitIndex = context.orbitIndex;
1977                String age = context.age;
1978                //String starType = context.star.getTypeId();
1979                String starType = star != null ? star.getTypeId() : null;
1980                if (context.center instanceof PlanetAPI) {
1981                        PlanetAPI star = (PlanetAPI) context.center;
1982                        if (star.isStar()) starType = star.getTypeId();
1983                }
1984                
1985                String parentCategory = context.parentCategory;
1986                
1987        
1988                WeightedRandomPicker<EntityGenDataSpec> picker = new WeightedRandomPicker<EntityGenDataSpec>(random);
1989                
1990                Collection<PlanetGenDataSpec> planetDataSpecs = Global.getSettings().getAllSpecs(PlanetGenDataSpec.class);
1991                for (PlanetGenDataSpec planetData : planetDataSpecs) {
1992                        if (!planetData.getCategory().equals(categoryData.getCategory())) continue;
1993                        
1994                        float offset = getHabOffset(planetData);
1995                        float minIndex = context.starData.getHabZoneStart() + planetData.getHabOffsetMin() + offset;
1996                        float maxIndex = context.starData.getHabZoneStart() + planetData.getHabOffsetMax() + offset;
1997                        boolean inRightRange = orbitIndex >= minIndex && orbitIndex <= maxIndex;
1998                        boolean giantMoonException = CAT_GIANT.equals(parentCategory) && 
1999                                                (planetData.hasTag(TAG_GIANT_MOON) && context.parent != null && context.parent.isGasGiant());
2000                        if (!inRightRange && !giantMoonException) continue;
2001                        
2002//                      if (planetData.getId().equals("rocky_unstable")) {
2003//                              System.out.println("dsfwefwefw");
2004//                      }
2005                        
2006                        boolean orbitIndexOk = fromParentOrbitIndex == 0 || !planetData.hasTag(TAG_FIRST_ORBIT_ONLY);
2007                        if (!orbitIndexOk) continue;
2008                        
2009                        boolean lagrangeStatusOk = COL_LAGRANGE.equals(extraMult) || !planetData.hasTag(TAG_LAGRANGE_ONLY);
2010                        if (!lagrangeStatusOk) continue;
2011                        
2012                        boolean nebulaStatusOk = NEBULA_NONE.equals(nebulaType) || !planetData.hasTag(TAG_NOT_IN_NEBULA);
2013                        nebulaStatusOk &= !NEBULA_NONE.equals(nebulaType) || !planetData.hasTag(TAG_REQUIRES_NEBULA);
2014                        nebulaStatusOk &= systemType != StarSystemType.NEBULA || !planetData.hasTag(TAG_NOT_NEBULA_UNLESS_MOON) || context.parent != null;
2015                        if (!nebulaStatusOk) continue;
2016                        
2017                        float weight = planetData.getFrequency();
2018                        if (age != null) weight *= planetData.getMultiplier(age);
2019                        if (starType != null) weight *= planetData.getMultiplier(starType);
2020                        if (parentCategory != null) weight *= planetData.getMultiplier(parentCategory);
2021                        for (String col : context.multipliers) {
2022                                weight *= planetData.getMultiplier(col);
2023                        }
2024                        if (extraMult != null) weight *= planetData.getMultiplier(extraMult);
2025                        if (weight > 0) picker.add(planetData, weight);
2026                }
2027                
2028                Collection<TerrainGenDataSpec> terrainDataSpecs = Global.getSettings().getAllSpecs(TerrainGenDataSpec.class);
2029                for (TerrainGenDataSpec terrainData : terrainDataSpecs) {
2030                        if (!terrainData.getCategory().equals(categoryData.getCategory())) continue;
2031                        
2032                        if (!nothingOk && terrainData.getId().equals("rings_nothing")) continue;
2033                        
2034                        float offset = getHabOffset(terrainData);
2035                        float minIndex = context.starData.getHabZoneStart() + terrainData.getHabOffsetMin() + offset;
2036                        float maxIndex = context.starData.getHabZoneStart() + terrainData.getHabOffsetMax() + offset;
2037                        boolean inRightRange = orbitIndex >= minIndex && orbitIndex <= maxIndex;
2038                        boolean giantMoonException = CAT_GIANT.equals(parentCategory) && 
2039                                                                (terrainData.hasTag(TAG_GIANT_MOON) && context.parent != null && context.parent.isGasGiant());
2040                        if (!inRightRange && !giantMoonException) continue;
2041                        
2042                        boolean orbitIndexOk = fromParentOrbitIndex == 0 || !terrainData.hasTag(TAG_FIRST_ORBIT_ONLY);
2043                        if (!orbitIndexOk) continue;
2044                        
2045                        boolean lagrangeStatusOk = COL_LAGRANGE.equals(extraMult) || !terrainData.hasTag(TAG_LAGRANGE_ONLY);
2046                        if (!lagrangeStatusOk) continue;
2047                        
2048                        boolean nebulaStatusOk = NEBULA_NONE.equals(nebulaType) || !terrainData.hasTag(TAG_NOT_IN_NEBULA);
2049                        nebulaStatusOk &= !NEBULA_NONE.equals(nebulaType) || !terrainData.hasTag(TAG_REQUIRES_NEBULA);
2050                        nebulaStatusOk &= systemType != StarSystemType.NEBULA || !terrainData.hasTag(TAG_NOT_NEBULA_UNLESS_MOON) || context.parent != null;
2051                        if (!nebulaStatusOk) continue;
2052                        
2053                        float weight = terrainData.getFrequency();
2054                        if (age != null) weight *= terrainData.getMultiplier(age);
2055                        if (starType != null) weight *= terrainData.getMultiplier(starType);
2056                        if (parentCategory != null) weight *= terrainData.getMultiplier(parentCategory);
2057                        for (String col : context.multipliers) {
2058                                weight *= terrainData.getMultiplier(col);
2059                        }
2060                        if (extraMult != null) weight *= terrainData.getMultiplier(extraMult);
2061                        if (weight > 0) picker.add(terrainData, weight);
2062                }
2063                
2064                return picker;
2065        }
2066        
2067
2068        
2069        protected void setDefaultLightColorBasedOnStars() {
2070                Color one = Color.white, two = null, three = null;
2071                
2072                switch (systemType) {
2073                case BINARY_FAR:
2074                case TRINARY_2FAR:
2075                case SINGLE:
2076                case NEBULA:
2077                        one = pickLightColorForStar(star);
2078                        break;
2079                case BINARY_CLOSE:
2080                case TRINARY_1CLOSE_1FAR:
2081                        one = pickLightColorForStar(star);
2082                        two = pickLightColorForStar(secondary);
2083                        break;
2084                case TRINARY_2CLOSE:
2085                        one = pickLightColorForStar(star);
2086                        two = pickLightColorForStar(secondary);
2087                        three = pickLightColorForStar(tertiary);
2088                        break;
2089                }
2090                
2091                Color result = one;
2092                if (two != null && three == null) {
2093                        result = Misc.interpolateColor(one, two, 0.5f);
2094                } else if (two != null && three != null) {
2095                        result = Misc.interpolateColor(one, two, 0.5f);
2096                        result = Misc.interpolateColor(result, three, 0.5f);
2097                }
2098                system.setLightColor(result); // light color in entire system, affects all entities
2099        }
2100        
2101        
2102        protected Color pickLightColorForStar(PlanetAPI star) {
2103                StarGenDataSpec starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, star.getSpec().getPlanetType(), false);
2104                Color min = starData.getLightColorMin();
2105                Color max = starData.getLightColorMax();
2106                Color lightColor = Misc.interpolateColor(min, max, random.nextFloat());
2107                return lightColor;
2108        }
2109        
2110        
2111//      protected void setLightColorBasedOnStar(PlanetAPI star) {
2112//              StarGenDataSpec starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, star.getSpec().getPlanetType(), false);
2113//              Color min = starData.getLightColorMin();
2114//              Color max = starData.getLightColorMax();
2115//              Color lightColor = Misc.interpolateColor(min, max, random.nextFloat());
2116//              system.setLightColor(lightColor); // light color in entire system, affects all entities
2117//      }
2118        
2119        
2120        protected PlanetAPI addRandomStar(String id, String name) {
2121                PlanetSpecAPI starSpec = pickStar(constellationAge);
2122                if (starSpec == null) return null;
2123                
2124                StarGenDataSpec starData = (StarGenDataSpec) Global.getSettings().getSpec(StarGenDataSpec.class, starSpec.getPlanetType(), false);
2125                float radius = getRadius(starData.getMinRadius(), starData.getMaxRadius());
2126                
2127                float corona = radius * (starData.getCoronaMult() + starData.getCoronaVar() * (random.nextFloat() - 0.5f));
2128                if (corona < starData.getCoronaMin()) corona = starData.getCoronaMin();
2129                
2130                PlanetAPI star = system.addPlanet(id, // unique id for this star
2131                                                                                  null,
2132                                                                                  name,
2133                                                                                    starSpec.getPlanetType(),  // id in planets.json
2134                                                                                    0f,  // angle
2135                                                                                    radius,               // radius (in pixels at default zoom)
2136                                                                                    10000f, // orbit radius
2137                                                                                    1000f // orbit days
2138                                                                                        );
2139                
2140                system.addCorona(star, corona,  // corona radius, from star edge 
2141                                                   starData.getSolarWind(),
2142                                                   (float) (starData.getMinFlare() + (starData.getMaxFlare() - starData.getMinFlare()) * random.nextFloat()),
2143                                                   starData.getCrLossMult());
2144                
2145                return star;
2146        }
2147        
2148
2149        
2150        public void init(StarSystemAPI system, StarAge age) {
2151                sector = Global.getSector();
2152                hyper = Global.getSector().getHyperspace();
2153                this.starAge = age;
2154                this.system = system;
2155                
2156        }
2157        protected boolean initSystem(String name, Vector2f loc) {
2158                sector = Global.getSector();
2159                system = sector.createStarSystem(name);
2160                system.setProcgen(true);
2161                system.setType(systemType);
2162                system.getLocation().set(loc);
2163                hyper = Global.getSector().getHyperspace();
2164                
2165                //system.setBackgroundTextureFilename("graphics/backgrounds/background2.jpg");
2166                system.setBackgroundTextureFilename(backgroundName);
2167                return true;
2168        }
2169
2170        protected void updateAgeAfterPickingStar() {
2171                starAge = starData.getAge();
2172                if (starAge == StarAge.ANY) {
2173                        starAge = constellationAge;
2174                }
2175                starAgeData = (AgeGenDataSpec) Global.getSettings().getSpec(AgeGenDataSpec.class, starAge.name(), true);
2176        }
2177        
2178        protected void cleanup() {
2179                if (system != null) {
2180                        Global.getSector().removeStarSystem(system);
2181                        system = null;
2182                }
2183        }
2184        
2185        public String getNebulaType() {
2186                return nebulaType;
2187        }
2188        
2189        public StarAge getConstellationAge() {
2190                return constellationAge;
2191        }
2192        
2193        public StarAge getStarAge() {
2194                return starAge;
2195        }
2196        
2197        
2198        public PlanetSpecAPI pickStar(StarAge age) {
2199                if (params != null && !params.starTypes.isEmpty()) {
2200                        String id = params.starTypes.remove(0);
2201                        //if (id.equals("black_hole") && systemType == StarSystemType.NEBULA) id = "nebula_center_old"; 
2202                        for (PlanetSpecAPI spec : Global.getSettings().getAllPlanetSpecs()) {
2203                                if (spec.getPlanetType().equals(id)) {
2204                                        Object test = Global.getSettings().getSpec(StarGenDataSpec.class, id, true);
2205                                        if (test == null) continue;
2206                                        StarGenDataSpec data = (StarGenDataSpec) test;
2207                                        boolean hasTag = data.hasTag(StarSystemType.NEBULA.name());
2208                                        boolean nebType = systemType == StarSystemType.NEBULA;
2209                                        boolean nebulaStatusOk = hasTag == nebType;
2210                                        if (nebulaStatusOk) {
2211                                                return spec;
2212                                        }
2213                                }
2214                        }
2215                        // doesn't work because the actual class the spec is registered under is PlanetSpec,
2216                        // not the API-exposed PlanetSpecAPI
2217                        //return (PlanetSpecAPI) Global.getSettings().getSpec(PlanetSpecAPI.class, id, true);
2218                }
2219                
2220                WeightedRandomPicker<PlanetSpecAPI> picker = new WeightedRandomPicker<PlanetSpecAPI>(random);
2221                for (PlanetSpecAPI spec : Global.getSettings().getAllPlanetSpecs()) {
2222                        if (!spec.isStar()) continue;
2223                        
2224                        String id = spec.getPlanetType();
2225                        Object test = Global.getSettings().getSpec(StarGenDataSpec.class, id, true);
2226                        if (test == null) continue;
2227                        StarGenDataSpec data = (StarGenDataSpec) test;
2228                        
2229                        
2230//                      boolean nebulaStatusOk = NEBULA_NONE.equals(nebulaType) || !data.hasTag(TAG_NOT_IN_NEBULA);
2231//                      nebulaStatusOk &= !NEBULA_NONE.equals(nebulaType) || !data.hasTag(TAG_REQUIRES_NEBULA);
2232//                      //nebulaStatusOk &= !NEBULA_NONE.equals(nebulaType) || !data.hasTag(StarSystemType.NEBULA.name());
2233//                      //nebulaStatusOk &= systemType != StarSystemType.NEBULA || data.hasTag(StarSystemType.NEBULA.name());
2234//                      nebulaStatusOk &= !data.hasTag(StarSystemType.NEBULA.name()) || systemType == StarSystemType.NEBULA;
2235                        
2236                        boolean hasTag = data.hasTag(StarSystemType.NEBULA.name());
2237                        boolean nebType = systemType == StarSystemType.NEBULA;
2238                        boolean nebulaStatusOk = hasTag == nebType; 
2239                        
2240                        if (!nebulaStatusOk) continue;
2241                        
2242                        //if (!id.equals("empty_nebula_center")) continue;
2243
2244                        float freq = 0f;
2245                        switch (age) {
2246                        case AVERAGE:
2247                                freq = data.getFreqAVERAGE();
2248                                break;
2249                        case OLD:
2250                                freq = data.getFreqOLD();
2251                                break;
2252                        case YOUNG:
2253                                freq = data.getFreqYOUNG();
2254                                break;
2255                        }
2256                        //System.out.println("Adding: " + spec.getPlanetType() + ", weight: " + freq);
2257                        picker.add(spec, freq);
2258                }
2259//              if (systemType == StarSystemType.NEBULA) {
2260//                      System.out.println("wfwefwe");
2261//              }
2262                return picker.pick();
2263        }
2264
2265        
2266        public static Color getColor(Color min, Color max) {
2267                Color color = new Color((int) (min.getRed() + (max.getRed() - min.getRed()) * random.nextDouble()),
2268                                (int) (min.getGreen() + (max.getGreen() - min.getGreen()) * random.nextDouble()),
2269                                (int) (min.getBlue() + (max.getBlue() - min.getBlue()) * random.nextDouble()),
2270                                255);
2271                
2272                return color;
2273        }
2274        
2275        
2276        
2277        public static float getNormalRandom(float min, float max) {
2278                return getNormalRandom(random, min, max);
2279        }
2280        
2281        public static float getNormalRandom(Random random, float min, float max) {
2282                double r = random.nextGaussian();
2283                r *= 0.2f;
2284                r += 0.5f;
2285                if (r < 0) r = 0;
2286                if (r > 1) r = 1;
2287                
2288                // 70% chance 0.3 < r < .7
2289                // 95% chance 0.1 < r < .7
2290                // 99% chance 0 < r < 1
2291                return min + (float) r * (max - min);
2292        }
2293        
2294        public static float getRadius(float min, float max) {
2295                float radius = (float) (min + (max - min) * random.nextFloat());
2296                return radius;
2297        }
2298        
2299        public static float getRandom(float min, float max) {
2300                float radius = (float) (min + (max - min) * random.nextFloat());
2301                return radius;
2302        }
2303        public Map<SectorEntityToken, PlanetAPI> getLagrangeParentMap() {
2304                return lagrangeParentMap;
2305        }
2306        
2307        public Map<SectorEntityToken, List<SectorEntityToken>> getAllEntitiesAdded() {
2308                return allNameableEntitiesAdded;
2309        }
2310
2311        public static Gender pickGender() {
2312                if (random.nextBoolean()) {
2313                        return Gender.MALE;
2314                }
2315                return Gender.FEMALE;
2316        }
2317}
2318
2319
2320
2321
2322
2323
2324
2325
2326