001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.HashSet;
007import java.util.LinkedHashMap;
008import java.util.List;
009import java.util.Random;
010import java.util.Set;
011
012import org.lwjgl.util.vector.Vector2f;
013
014import com.fs.starfarer.api.Global;
015import com.fs.starfarer.api.campaign.LocationAPI;
016import com.fs.starfarer.api.campaign.PlanetAPI;
017import com.fs.starfarer.api.campaign.SectorEntityToken;
018import com.fs.starfarer.api.campaign.StarSystemAPI;
019import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel;
020import com.fs.starfarer.api.campaign.econ.MarketConditionAPI;
021import com.fs.starfarer.api.impl.campaign.ids.Commodities;
022import com.fs.starfarer.api.impl.campaign.ids.Conditions;
023import com.fs.starfarer.api.impl.campaign.ids.Entities;
024import com.fs.starfarer.api.impl.campaign.ids.Factions;
025import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
026import com.fs.starfarer.api.impl.campaign.ids.Tags;
027import com.fs.starfarer.api.impl.campaign.intel.events.ht.HTPoints;
028import com.fs.starfarer.api.impl.campaign.procgen.Constellation;
029import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
030import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.DomainSurveyDerelictSpecial.DomainSurveyDerelictSpecialData;
031import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.DomainSurveyDerelictSpecial.SpecialType;
032import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SurveyDataSpecial.SurveyDataSpecialData;
033import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SurveyDataSpecial.SurveyDataSpecialType;
034import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.TopographicDataSpecial.TopographicDataSpecialData;
035import com.fs.starfarer.api.plugins.SurveyPlugin;
036import com.fs.starfarer.api.util.Misc;
037import com.fs.starfarer.api.util.WeightedRandomPicker;
038
039
040public class DerelictThemeGenerator extends BaseThemeGenerator {
041
042        public static final float BASE_LINK_FRACTION = 0.25f;
043        public static final float SALVAGE_SPECIAL_FRACTION = 0.5f;
044        public static final float TOPOGRAPHIC_DATA_FRACTION = 0.2f;
045        
046        public static final int BRANCHES_PER_MOTHERSHIP_MIN = 3;
047        public static final int BRANCHES_PER_MOTHERSHIP_MAX = 4;
048        
049        public static final int BRANCHES_PER_SHIP_MIN = 2;
050        public static final int BRANCHES_PER_SHIP_MAX = 3;
051        
052        
053        public static class SystemGenData {
054                public int numMotherships;
055                public int numSurveyShips;
056                public int numProbes;
057        }
058        
059        
060        public String getThemeId() {
061                return Themes.DERELICTS;
062        }
063
064        @Override
065        public void generateForSector(ThemeGenContext context, float allowedUnusedFraction) {
066                
067                float total = (float) (context.constellations.size() - context.majorThemes.size()) * allowedUnusedFraction;
068                if (total <= 0) return;
069                
070                float avg1 = (BRANCHES_PER_MOTHERSHIP_MIN + BRANCHES_PER_MOTHERSHIP_MAX) / 2f;
071                float avg2 = (BRANCHES_PER_SHIP_MIN + BRANCHES_PER_SHIP_MAX) / 2f;
072                float perChain = 1 + avg1 + (avg1 * avg2);
073                
074                float num = total / perChain;
075                if (num < 1) num = 1;
076                if (num > 3) num = 3;
077                
078                if (num > 1 && num < 2) {
079                        num = 2;
080                } else {
081                        num = Math.round(num);
082                }
083                
084                List<AddedEntity> mothershipsSoFar = new ArrayList<AddedEntity>();
085                
086                for (int i = 0; i < num; i++) {
087                        addMothershipChain(context, mothershipsSoFar);
088                }
089                
090                
091                
092                WeightedRandomPicker<StarSystemAPI> cryoSystems = new WeightedRandomPicker<StarSystemAPI>(StarSystemGenerator.random);
093                WeightedRandomPicker<StarSystemAPI> backup = new WeightedRandomPicker<StarSystemAPI>(StarSystemGenerator.random);
094                OUTER: for (StarSystemAPI system : Global.getSector().getStarSystems()) {
095                        float w = 0f;
096                        if (system.hasTag(Tags.THEME_DERELICT_PROBES)) {
097                                w = 10f;
098                        } else if (system.hasTag(Tags.THEME_DERELICT_SURVEY_SHIP)) {
099                                w = 10f;
100                        } else if (system.hasTag(Tags.THEME_DERELICT_MOTHERSHIP)) {
101                                w = 10f;
102                        } else if (system.hasTag(Tags.THEME_DERELICT)) {
103                                w = 10f;
104                        } else {
105                                continue;
106                        }
107                        
108                        int numPlanets = 0;
109                        boolean hasHab = false;
110                        for (PlanetAPI planet : system.getPlanets()) {
111                                if (planet.isStar()) continue;
112                                if (planet.getSpec().isPulsar()) continue OUTER;
113                                hasHab |= planet.getMarket() != null && planet.getMarket().hasCondition(Conditions.HABITABLE);
114                                numPlanets++;
115                        }
116
117                        WeightedRandomPicker<StarSystemAPI> use = cryoSystems;
118                        if (!hasHab || numPlanets < 3) {
119                                use = backup;
120                        }
121                        
122                        if (hasHab) w += 5;
123                        w += numPlanets;
124                        
125                        if (use == backup) {
126                                w *= 0.0001f;
127                        }
128                        use.add(system, w);
129                }
130                
131                int numCryo = 2;
132                if (cryoSystems.isEmpty() || cryoSystems.getItems().size() < numCryo + 1) {
133                        cryoSystems.addAll(backup);
134                }
135                
136                int added = 0;
137                WeightedRandomPicker<String> cryosleeperNames = new WeightedRandomPicker<String>(random);
138                cryosleeperNames.add("Calypso");
139                cryosleeperNames.add("Tantalus");
140                while (added < numCryo && !cryoSystems.isEmpty()) {
141                        StarSystemAPI pick = cryoSystems.pickAndRemove();
142                        String name = cryosleeperNames.pickAndRemove();
143                        AddedEntity cryo = addCryosleeper(pick, name);
144                        if (cryo != null) {
145                                added++;
146                        }
147                }
148        }
149        
150        protected void addMothershipChain(ThemeGenContext context, List<AddedEntity> mothershipsSoFar) {
151                
152                List<AddedEntity> all = new ArrayList<AddedEntity>(); 
153                
154                
155                Vector2f center = new Vector2f();
156                for (AddedEntity e : mothershipsSoFar) {
157                        Vector2f.add(center, e.entity.getLocationInHyperspace(), center);
158                }
159                center.scale(1f / (float)(mothershipsSoFar.size() + 1f));
160                
161                List<Constellation> constellations = getSortedAvailableConstellations(context, false, center, null);
162                WeightedRandomPicker<Constellation> picker = new WeightedRandomPicker<Constellation>(StarSystemGenerator.random);
163                for (int i = 0; i < constellations.size() / 3; i++) {
164                        picker.add(constellations.get(i));
165                }
166                
167                Constellation main = picker.pick();
168                if (main == null) return;
169                
170                if (DEBUG) {
171                        System.out.println("Picked for mothership chain start: [" + main.getNameWithType() + "] (" + (int)main.getLocation().x + ", " + (int)main.getLocation().y + ")");
172                }
173                
174                constellations.remove(main);
175                context.majorThemes.put(main, Themes.DERELICTS);
176
177                
178                StarSystemAPI mainSystem = main.getSystemWithMostPlanets();
179                if (mainSystem == null) return;
180                
181                //mainSystem.addTag(Tags.THEME_DERELICT);
182                for (StarSystemAPI system : main.getSystems()) {
183                        system.addTag(Tags.THEME_DERELICT);
184                }
185                
186//              if (mainSystem.getName().toLowerCase().contains("valac")) {
187//                      System.out.println("HERE13123123123");
188//              }
189                
190                AddedEntity mothership = addMothership(mainSystem);
191                if (mothership == null) return;
192                
193                all.add(mothership);
194                
195                if (DEBUG) {
196                        System.out.println("  Added mothership to [" + mainSystem.getNameWithLowercaseType() + "]");
197                }
198                
199                //if (true) return;
200                
201                // probes in mothership system
202                //int probesNearMothership = (int) Math.round(StarSystemGenerator.getRandom(2, 4));
203                int probesNearMothership = getNumProbesForSystem(mothership.entity.getContainingLocation());
204                List<AddedEntity> added = addToSystem(mainSystem, Entities.DERELICT_SURVEY_PROBE, probesNearMothership);
205                all.addAll(added);
206                
207                
208                linkFractionToParent(mothership, added, 
209                                                         BASE_LINK_FRACTION,
210                                                         SpecialType.LOCATION_MOTHERSHIP);
211                
212                // survey ships in mothership constellation
213                int surveyShipsNearMothership = (int) Math.round(StarSystemGenerator.getRandom(0, 3));
214                if (surveyShipsNearMothership > main.getSystems().size()) surveyShipsNearMothership = main.getSystems().size();
215                if (DEBUG) {
216                        System.out.println(String.format("Adding %d survey ships near mothership", surveyShipsNearMothership));
217                }
218                List<AddedEntity> addedShips = addToConstellation(main, Entities.DERELICT_SURVEY_SHIP, surveyShipsNearMothership, false);
219                all.addAll(addedShips);
220                
221                // probes in each system with survey ship
222                for (AddedEntity e : addedShips) {
223                        int probesNearSurveyShip = (int) Math.round(StarSystemGenerator.getRandom(1, 3));
224                        //int probesNearSurveyShip = getNumProbesForSystem(e.entity.getContainingLocation());
225                        added = addProbes((StarSystemAPI) e.entity.getContainingLocation(), probesNearSurveyShip);
226                        all.addAll(added);
227                        
228                        linkFractionToParent(e, added, 
229                                         BASE_LINK_FRACTION,
230                                         SpecialType.LOCATION_SURVEY_SHIP);
231                }
232                
233                linkFractionToParent(mothership, addedShips, 
234                                                         BASE_LINK_FRACTION,
235                                                         SpecialType.LOCATION_MOTHERSHIP);
236                
237                //if (true) return;
238                
239                constellations = getSortedAvailableConstellations(context, false, mothership.entity.getLocationInHyperspace(), null);
240                picker = new WeightedRandomPicker<Constellation>(StarSystemGenerator.random);
241                for (int i = constellations.size() - 7; i < constellations.size(); i++) {
242                        if (i < 0) continue;
243                        picker.add(constellations.get(i));
244                }
245
246                int numSurveyShipsInNearConstellations = (int) Math.round(StarSystemGenerator.getRandom(BRANCHES_PER_MOTHERSHIP_MIN, BRANCHES_PER_MOTHERSHIP_MAX));
247                if (DEBUG) {
248                        System.out.println(String.format("Adding up to %d survey ships", numSurveyShipsInNearConstellations));
249                }
250                List<Constellation> constellationsForSurveyShips = new ArrayList<Constellation>();
251                for (int i = 0; i < numSurveyShipsInNearConstellations && !picker.isEmpty(); i++) {
252                        constellationsForSurveyShips.add(picker.pickAndRemove());
253                }
254                List<AddedEntity> outerShips = new ArrayList<AddedEntity>();
255                
256                
257                for (Constellation c : constellationsForSurveyShips) {
258                        context.majorThemes.put(c, Themes.DERELICTS);
259                        
260                        if (DEBUG) {
261                                System.out.println("  Picked for survey ship: [" + c.getNameWithType() + "]");
262                        }
263                        
264                        addedShips = addToConstellation(c, Entities.DERELICT_SURVEY_SHIP, 1, true);
265                        if (addedShips.isEmpty()) continue;
266                        
267                        all.addAll(addedShips);
268                        
269                        AddedEntity ship = addedShips.get(0);
270                        outerShips.addAll(addedShips);
271                        
272                        //int probesNearSurveyShip = (int) Math.round(StarSystemGenerator.getRandom(1, 3));
273                        int probesNearSurveyShip = getNumProbesForSystem(ship.entity.getContainingLocation());
274                        added = addProbes((StarSystemAPI) ship.entity.getContainingLocation(), probesNearSurveyShip);
275                        all.addAll(added);
276                        
277                        linkFractionToParent(ship, added, 
278                                                                 BASE_LINK_FRACTION,
279                                                                 SpecialType.LOCATION_SURVEY_SHIP);
280                        
281                        int probesInSameConstellation = (int) Math.round(StarSystemGenerator.getRandom(2, 5));
282                        int max = c.getSystems().size() + 2;
283                        if (probesInSameConstellation > max) probesInSameConstellation = max;
284                        
285                        added = addToConstellation(c, Entities.DERELICT_SURVEY_PROBE, probesInSameConstellation, false);
286                        all.addAll(added);
287                        
288                        linkFractionToParent(ship, added, 
289                                                                 BASE_LINK_FRACTION,
290                                                                 SpecialType.LOCATION_SURVEY_SHIP);
291                        
292                        
293                        
294                        List<Constellation> c2 = getSortedAvailableConstellations(context, false, c.getLocation(), constellationsForSurveyShips);
295                        WeightedRandomPicker<Constellation> p2 = new WeightedRandomPicker<Constellation>(StarSystemGenerator.random);
296                        //for (int i = 0; i < constellations.size() / 3 && i < 7; i++) {
297                        for (int i = c2.size() - 3; i < c2.size(); i++) {
298                                if (i < 0) continue;
299                                p2.add(constellations.get(i));
300                        }
301                        
302                        int probeSystemsNearShip = (int) Math.round(StarSystemGenerator.getRandom(BRANCHES_PER_SHIP_MIN, BRANCHES_PER_SHIP_MAX));
303                        int k = 0;
304                        if (DEBUG) {
305                                System.out.println(String.format("Adding probes to %d constellations near survey ship", probeSystemsNearShip));
306                        }
307                        List<AddedEntity> probes3 = new ArrayList<AddedEntity>();
308                        while (k < probeSystemsNearShip && !p2.isEmpty()) {
309                                Constellation pick = p2.pickAndRemove();
310                                k++;
311                                context.majorThemes.put(pick, Themes.NO_THEME);
312                                int probesInConstellation = (int) Math.round(StarSystemGenerator.getRandom(1, 3));
313                                probes3.addAll(addToConstellation(pick, Entities.DERELICT_SURVEY_PROBE, probesInConstellation, false));
314                        }
315                        
316                        all.addAll(probes3);
317                        linkFractionToParent(ship, probes3, 
318                                         BASE_LINK_FRACTION,
319                                         SpecialType.LOCATION_MOTHERSHIP);
320                }
321                
322                linkFractionToParent(mothership, outerShips, 
323                                                         BASE_LINK_FRACTION,
324                                                         SpecialType.LOCATION_MOTHERSHIP);
325                
326
327                
328                assignRandomSpecials(all);
329        }
330        
331        public static Set<String> interestingConditions = new HashSet<String>();
332        public static Set<String> interestingConditionsWithoutHabitable = new HashSet<String>();
333        public static Set<String> interestingConditionsWithRuins = new HashSet<String>();
334        static {
335                //interestingConditions.add(Conditions.VOLATILES_ABUNDANT);
336                interestingConditions.add(Conditions.VOLATILES_PLENTIFUL);
337                //interestingConditions.add(Conditions.ORE_RICH);
338                interestingConditions.add(Conditions.RARE_ORE_RICH);
339                interestingConditions.add(Conditions.RARE_ORE_ULTRARICH);
340                interestingConditions.add(Conditions.ORE_ULTRARICH);
341                interestingConditions.add(Conditions.FARMLAND_BOUNTIFUL);
342                interestingConditions.add(Conditions.FARMLAND_ADEQUATE);
343                //interestingConditions.add(Conditions.ORGANICS_ABUNDANT);
344                interestingConditions.add(Conditions.ORGANICS_PLENTIFUL);
345                interestingConditions.add(Conditions.HABITABLE);
346                
347                interestingConditionsWithoutHabitable.addAll(interestingConditions);
348                interestingConditionsWithoutHabitable.remove(Conditions.HABITABLE);
349                
350                interestingConditionsWithRuins.addAll(interestingConditions);
351                interestingConditionsWithRuins.add(Conditions.RUINS_VAST);
352                interestingConditionsWithRuins.add(Conditions.RUINS_EXTENSIVE);
353        }
354
355        
356        
357        protected void assignRandomSpecials(List<AddedEntity> entities) {
358                Set<PlanetAPI> usedPlanets = new HashSet<PlanetAPI>();
359                Set<StarSystemAPI> usedSystems = new HashSet<StarSystemAPI>();
360                
361                for (AddedEntity e : entities) {
362                        if (hasSpecial(e.entity)) continue;
363                        
364                        SurveyDataSpecialType type = null;
365                        
366                        if (StarSystemGenerator.random.nextFloat() < TOPOGRAPHIC_DATA_FRACTION) {
367                                int min = 0;
368                                int max = 0;
369                                if (Entities.DERELICT_SURVEY_PROBE.equals(e.entityType)) {
370                                        min = HTPoints.LOW_MIN;
371                                        max = HTPoints.LOW_MAX;
372                                } else if (Entities.DERELICT_SURVEY_SHIP.equals(e.entityType)) {
373                                        min = HTPoints.MEDIUM_MIN;
374                                        max = HTPoints.MEDIUM_MAX;
375                                } else if (Entities.DERELICT_MOTHERSHIP.equals(e.entityType)) {
376                                        min = HTPoints.HIGH_MIN;
377                                        max = HTPoints.HIGH_MAX;
378                                }
379                                int points = min + StarSystemGenerator.random.nextInt(max - min + 1);
380                                if (points > 0) {
381                                        TopographicDataSpecialData data = new TopographicDataSpecialData(points);
382                                        Misc.setSalvageSpecial(e.entity, data);
383                                        continue;
384                                }
385                        }
386                        if (StarSystemGenerator.random.nextFloat() < SALVAGE_SPECIAL_FRACTION) {
387                                float pNothing = 0.1f;
388                                if (Entities.DERELICT_SURVEY_PROBE.equals(e.entityType)) {
389                                        pNothing = 0.5f;
390                                } else if (Entities.DERELICT_SURVEY_SHIP.equals(e.entityType)) {
391                                        pNothing = 0.25f;
392                                } else if (Entities.DERELICT_MOTHERSHIP.equals(e.entityType)) {
393                                        pNothing = 0f;
394                                }
395                                
396                                float r = StarSystemGenerator.random.nextFloat();
397                                if (r >= pNothing) {
398                                        type = SurveyDataSpecialType.PLANET_SURVEY_DATA;
399                                }
400                        }
401                        
402                        //type = SpecialType.PLANET_SURVEY_DATA;
403                                
404                        if (type == SurveyDataSpecialType.PLANET_SURVEY_DATA) {
405                                PlanetAPI planet = findInterestingPlanet(e.entity.getConstellation().getSystems(), usedPlanets);
406                                if (planet != null) {
407                                        SurveyDataSpecialData data = new SurveyDataSpecialData(SurveyDataSpecialType.PLANET_SURVEY_DATA);
408                                        data.entityId = planet.getId();
409                                        data.includeRuins = false;
410                                        Misc.setSalvageSpecial(e.entity, data);
411                                        usedPlanets.add(planet);
412                                        
413//                                      DomainSurveyDerelictSpecialData special = new DomainSurveyDerelictSpecialData(type);
414//                                      special.entityId = planet.getId();
415//                                      usedPlanets.add(planet);
416//                                      e.entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SPECIAL_DATA, special);
417                                }
418                        }
419                }
420        }
421        
422        
423        public static StarSystemAPI findNearbySystem(SectorEntityToken from, Set<StarSystemAPI> exclude) {
424                return findNearbySystem(from, exclude, null, 10000f);
425        }
426        
427        public static StarSystemAPI findNearbySystem(SectorEntityToken from, Set<StarSystemAPI> exclude, Random random, float maxRange) {
428                if (random == null) random = StarSystemGenerator.random;
429                
430                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(random);
431                
432                for (StarSystemAPI system : Global.getSector().getStarSystems()) {
433                        if (exclude != null && exclude.contains(system)) continue;
434                        
435                        float dist = Misc.getDistance(from.getLocationInHyperspace(), system.getLocation());
436                        if (dist > maxRange) continue;
437                        if (systemIsEmpty(system)) continue;
438                        
439                        picker.add(system);
440                }
441                
442                return picker.pick();
443        }
444        
445        
446        public static String getInterestingCondition(PlanetAPI planet, boolean includeRuins) {
447                if (planet == null) return null;
448                
449                Set<String> conditions = interestingConditions;
450                if (includeRuins) conditions = interestingConditionsWithRuins;
451                
452                for (MarketConditionAPI mc : planet.getMarket().getConditions()) {
453                        if (conditions.contains(mc.getId())) {
454                                return mc.getId();
455                        }
456                }
457                return null;
458        }
459        
460        public static PlanetAPI findInterestingPlanet(List<StarSystemAPI> systems, Set<PlanetAPI> exclude) {
461                return findInterestingPlanet(systems, exclude, true, false, null);
462        }
463        public static PlanetAPI findInterestingPlanet(List<StarSystemAPI> systems, Set<PlanetAPI> exclude, boolean includeKnown, boolean includeRuins, Random random) {
464                if (random == null) random = StarSystemGenerator.random;
465                
466                WeightedRandomPicker<PlanetAPI> planets = new WeightedRandomPicker<PlanetAPI>(random);
467
468                Set<String> conditions = interestingConditions;
469                if (includeRuins) conditions = interestingConditionsWithRuins;
470                
471                SurveyPlugin plugin = (SurveyPlugin) Global.getSettings().getNewPluginInstance("surveyPlugin");
472                
473                for (StarSystemAPI system : systems) {
474                        if (system.hasTag(Tags.THEME_HIDDEN)) continue;
475                        
476                        for (PlanetAPI planet : system.getPlanets()) {
477                                if (planet.isStar()) continue;
478                                if (exclude != null && exclude.contains(planet)) continue;
479                                if (planet.getMarket() == null || !planet.getMarket().isPlanetConditionMarketOnly()) continue;
480                                if (!includeKnown && planet.getMarket() != null && planet.getMarket().getSurveyLevel() == SurveyLevel.FULL) {
481                                        continue;
482                                }
483                                //if (planet.getMarket().getSurveyLevel() == SurveyLevel.FULL) continue;
484                                
485                                String type = plugin.getSurveyDataType(planet);
486                                boolean classIV = Commodities.SURVEY_DATA_4.equals(type);
487                                boolean classV = Commodities.SURVEY_DATA_5.equals(type);
488                                
489                                if (!(classIV || classV || planet.getMarket().getHazardValue() <= 1f)) continue;
490                                
491                                float w = 1f;
492                                for (MarketConditionAPI mc : planet.getMarket().getConditions()) {
493                                        if (conditions.contains(mc.getId())) {
494                                                w += 1f;
495                                        }
496                                }
497                                if (classIV) w *= 0.5f;
498                                if (classV) w *= 4f; 
499                                planets.add(planet, w);
500                        }
501                }
502                return planets.pick();
503        }
504        
505        
506        protected int getNumProbesForSystem(LocationAPI system) {
507                int base = 1;
508                int planets = system.getPlanets().size();
509
510                if (planets <= 3) {
511                } else if (planets <= 5) {
512                        base += 1;
513                } else if (planets <= 8) {
514                        base += 2;
515                } else {
516                        base += 3;
517                }
518                
519                base += StarSystemGenerator.random.nextInt(2);
520                
521                return base;
522        }
523        protected void linkFractionToParent(AddedEntity parent, List<AddedEntity> children, float p, SpecialType type) {
524                
525                WeightedRandomPicker<AddedEntity> picker = new WeightedRandomPicker<AddedEntity>(StarSystemGenerator.random);
526                for (AddedEntity c : children) {
527                        if (!hasSpecial(c.entity)) {
528                                picker.add(c);
529                        }
530                }
531                
532                int extraLinks = (int) Math.max(1, Math.round(children.size() * p * (1f + StarSystemGenerator.random.nextFloat() * 0.5f)));
533                for (int i = 0; i < extraLinks && !picker.isEmpty(); i++) {
534                        AddedEntity e = picker.pickAndRemove();
535                        linkToParent(e.entity, parent.entity, type);
536                }
537        }
538        
539//      protected AddedEntity getClosest(AddedEntity from, List<AddedEntity> choices) {
540//              float min = Float.MAX_VALUE;
541//              AddedEntity result = null;
542//              for (AddedEntity e : choices) {
543//                      
544//              }
545//              
546//              return result;
547//      }
548        
549        protected void linkToParent(SectorEntityToken from, SectorEntityToken parent, SpecialType type) {
550                if (hasSpecial(from)) return;
551                
552                DomainSurveyDerelictSpecialData special = new DomainSurveyDerelictSpecialData(type);
553                special.entityId = parent.getId();
554                from.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SPECIAL_DATA, special);
555        }
556        
557        protected void linkToMothership(SectorEntityToken from, SectorEntityToken mothership) {
558                if (hasSpecial(from)) return;
559                
560                DomainSurveyDerelictSpecialData special = new DomainSurveyDerelictSpecialData(SpecialType.LOCATION_MOTHERSHIP);
561                special.entityId = mothership.getId();
562                from.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SPECIAL_DATA, special);
563        }
564        
565        public static boolean hasSpecial(SectorEntityToken entity) {
566                return entity.getMemoryWithoutUpdate().contains(MemFlags.SALVAGE_SPECIAL_DATA);
567        }
568        
569        protected List<AddedEntity> addToConstellation(Constellation c, String type, int num, boolean biggestFirst) {
570                List<AddedEntity> result = new ArrayList<AddedEntity>();
571                
572                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(StarSystemGenerator.random);
573                picker.addAll(c.getSystems());
574                
575                boolean first = true;
576                for (int i = 0; i < num; i++) {
577                        StarSystemAPI system = picker.pick();
578                        if (biggestFirst && first) system = c.getSystemWithMostPlanets();
579                        first = false;
580                        
581                        if (system == null) continue;
582                        
583                        result.addAll(addToSystem(system, type, 1));
584                }
585                
586                return result;
587        }
588        
589        protected List<AddedEntity> addToSystem(StarSystemAPI system, String type, int num) {
590                List<AddedEntity> result = new ArrayList<AddedEntity>();
591                if (system == null) return result;
592                
593                for (int i = 0; i < num; i++) {
594                        AddedEntity e = null;
595                        if (Entities.DERELICT_MOTHERSHIP.equals(type)) {
596                                e = addMothership(system);
597                        } else if (Entities.DERELICT_SURVEY_SHIP.equals(type)) {
598                                e = addSurveyShip(system);
599                        } else if (Entities.DERELICT_SURVEY_PROBE.equals(type)) {
600                                result.addAll(addProbes(system, 1));
601                        }
602                        if (e != null) {
603                                result.add(e);
604                        }
605                }
606                return result;
607        }
608        
609        
610        
611        protected AddedEntity addMothership(StarSystemAPI system) {
612                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
613                weights.put(LocationType.PLANET_ORBIT, 10f);
614                weights.put(LocationType.JUMP_ORBIT, 1f);
615                weights.put(LocationType.NEAR_STAR, 1f);
616                weights.put(LocationType.OUTER_SYSTEM, 5f);
617                weights.put(LocationType.IN_ASTEROID_BELT, 10f);
618                weights.put(LocationType.IN_RING, 10f);
619                weights.put(LocationType.IN_ASTEROID_FIELD, 10f);
620                weights.put(LocationType.STAR_ORBIT, 1f);
621                weights.put(LocationType.IN_SMALL_NEBULA, 1f);
622                weights.put(LocationType.L_POINT, 1f);
623                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, 100f, weights);
624                
625//              if (system.getName().toLowerCase().contains("valac")) {
626//                      for (int i = 0; i < 10; i++) {
627//                              //Random random = new Random(32895278947689263L);
628//                              StarSystemGenerator.random = new Random(32895278947689263L);
629//                              random = StarSystemGenerator.random;
630//                              locs = getLocations(random, system, 100f, weights);
631//                              EntityLocation loc = locs.pickAndRemove();
632//                              System.out.println("Location: " + loc.toString());
633//                      }
634//              }
635                
636                AddedEntity entity = addEntity(random, system, locs, Entities.DERELICT_MOTHERSHIP, Factions.DERELICT);
637                if (entity != null) {
638                        system.addTag(Tags.THEME_INTERESTING);
639                        system.addTag(Tags.THEME_DERELICT);
640                        system.addTag(Tags.THEME_DERELICT_MOTHERSHIP);
641                }
642                
643                if (DEBUG) {
644                        if (entity != null) {
645                                System.out.println(String.format("  Added mothership to %s", system.getNameWithLowercaseType()));
646                        } else {
647                                System.out.println(String.format("  Failed to add mothership to %s", system.getNameWithLowercaseType()));
648                        }
649                }
650                return entity;
651        }
652        
653        
654        protected AddedEntity addCryosleeper(StarSystemAPI system, String name) {
655                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
656                weights.put(LocationType.PLANET_ORBIT, 10f);
657                weights.put(LocationType.JUMP_ORBIT, 1f);
658                weights.put(LocationType.NEAR_STAR, 1f);
659                weights.put(LocationType.OUTER_SYSTEM, 5f);
660                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
661                weights.put(LocationType.IN_RING, 5f);
662                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
663                weights.put(LocationType.STAR_ORBIT, 5f);
664                weights.put(LocationType.IN_SMALL_NEBULA, 5f);
665                weights.put(LocationType.L_POINT, 10f);
666                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, 100f, weights);
667                
668                
669                AddedEntity entity = addEntity(random, system, locs, Entities.DERELICT_CRYOSLEEPER, Factions.DERELICT);
670                if (entity != null) {
671                        system.addTag(Tags.THEME_INTERESTING);
672                        system.addTag(Tags.THEME_DERELICT);
673                        system.addTag(Tags.THEME_DERELICT_CRYOSLEEPER);
674                        
675                        if (name != null) {
676                                entity.entity.setName(entity.entity.getName() + " \"" + name + "\"");
677                                //entity.entity.setName("Cryosleeper" + "\"" + name + "\"");
678                        }
679                }
680                
681                if (DEBUG) {
682                        if (entity != null) {
683                                System.out.println(String.format("  Added cryosleeper to %s", system.getNameWithLowercaseType()));
684                        } else {
685                                System.out.println(String.format("  Failed to add cryosleeper to %s", system.getNameWithLowercaseType()));
686                        }
687                }
688                return entity;
689        }
690        
691        protected AddedEntity addSurveyShip(StarSystemAPI system) {
692                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
693                weights.put(LocationType.PLANET_ORBIT, 10f);
694                weights.put(LocationType.JUMP_ORBIT, 1f);
695                weights.put(LocationType.NEAR_STAR, 1f);
696                weights.put(LocationType.OUTER_SYSTEM, 5f);
697                weights.put(LocationType.IN_ASTEROID_BELT, 10f);
698                weights.put(LocationType.IN_RING, 10f);
699                weights.put(LocationType.IN_ASTEROID_FIELD, 10f);
700                weights.put(LocationType.STAR_ORBIT, 1f);
701                weights.put(LocationType.IN_SMALL_NEBULA, 1f);
702                weights.put(LocationType.L_POINT, 1f);
703                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, 100f, weights);
704                
705                AddedEntity entity = addEntity(random, system, locs, Entities.DERELICT_SURVEY_SHIP, Factions.DERELICT);
706                
707                if (entity != null) {
708                        system.addTag(Tags.THEME_INTERESTING);
709                        system.addTag(Tags.THEME_DERELICT);
710                        system.addTag(Tags.THEME_DERELICT_SURVEY_SHIP);
711                }
712                
713                if (DEBUG) {
714                        if (entity != null) {
715                                System.out.println(String.format("  Added survey ship to %s", system.getNameWithLowercaseType()));
716                        } else {
717                                System.out.println(String.format("  Failed to add survey ship to %s", system.getNameWithLowercaseType()));
718                        }
719                }
720                return entity;
721        }
722        
723        protected List<AddedEntity> addProbes(StarSystemAPI system, int num) {
724                LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
725                weights.put(LocationType.PLANET_ORBIT, 20f);
726                weights.put(LocationType.JUMP_ORBIT, 10f);
727                weights.put(LocationType.NEAR_STAR, 10f);
728                weights.put(LocationType.OUTER_SYSTEM, 5f);
729                weights.put(LocationType.IN_ASTEROID_BELT, 5f);
730                weights.put(LocationType.IN_RING, 5f);
731                weights.put(LocationType.IN_ASTEROID_FIELD, 5f);
732                weights.put(LocationType.STAR_ORBIT, 1f);
733                weights.put(LocationType.IN_SMALL_NEBULA, 1f);
734                weights.put(LocationType.L_POINT, 1f);
735                WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, 100f, weights);
736                
737                List<AddedEntity> result = new ArrayList<AddedEntity>();
738                for (int i = 0; i < num; i++) {
739                        AddedEntity probe = addEntity(random, system, locs, Entities.DERELICT_SURVEY_PROBE, Factions.DERELICT);
740                        if (probe != null) {
741                                result.add(probe);
742                                
743                                system.addTag(Tags.THEME_INTERESTING_MINOR);
744                                system.addTag(Tags.THEME_DERELICT);
745                                system.addTag(Tags.THEME_DERELICT_PROBES);
746                        }
747                        
748                        if (DEBUG) {
749                                if (probe != null) {
750                                        System.out.println(String.format("  Added probe to %s", system.getNameWithLowercaseType()));
751                                } else {
752                                        System.out.println(String.format("  Failed to add probe to %s", system.getNameWithLowercaseType()));
753                                }
754                        }
755                }
756                return result;
757        }
758
759        
760        /**
761         * Sorted by *descending* distance from sortFrom.
762         * @param context
763         * @param sortFrom
764         * @return
765         */
766        protected List<Constellation> getSortedAvailableConstellations(ThemeGenContext context, boolean emptyOk, final Vector2f sortFrom, List<Constellation> exclude) {
767                List<Constellation> constellations = new ArrayList<Constellation>();
768                for (Constellation c : context.constellations) {
769                        if (context.majorThemes.containsKey(c)) continue;
770                        if (!emptyOk && constellationIsEmpty(c)) continue;
771                        
772                        constellations.add(c);
773                }
774                
775                if (exclude != null) {
776                        constellations.removeAll(exclude);
777                }
778                
779                Collections.sort(constellations, new Comparator<Constellation>() {
780                        public int compare(Constellation o1, Constellation o2) {
781                                float d1 = Misc.getDistance(o1.getLocation(), sortFrom);
782                                float d2 = Misc.getDistance(o2.getLocation(), sortFrom);
783                                return (int) Math.signum(d2 - d1);
784                        }
785                });
786                return constellations;
787        }
788        
789        
790        public static boolean constellationIsEmpty(Constellation c) {
791                for (StarSystemAPI s : c.getSystems()) {
792                        if (!systemIsEmpty(s)) return false;
793                }
794                return true;
795        }
796        public static boolean systemIsEmpty(StarSystemAPI system) {
797                for (PlanetAPI p : system.getPlanets()) {
798                        if (!p.isStar()) return false;
799                }
800                //system.getTerrainCopy().isEmpty()
801                return true;
802        }
803        
804        
805        
806        
807        
808        
809        
810        
811        
812//      public List<AddedEntity> generateForSystem(StarSystemAPI system, SystemGenData data) {
813//              if (data == null) return new ArrayList<AddedEntity>();
814//              
815//              LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>();
816//              weights.put(LocationType.PLANET_ORBIT, 1f);
817//              weights.put(LocationType.JUMP_ORBIT, 1f);
818//              weights.put(LocationType.NEAR_STAR, 1f);
819//              weights.put(LocationType.OUTER_SYSTEM, 1f);
820//              weights.put(LocationType.IN_ASTEROID_BELT, 1f);
821//              weights.put(LocationType.IN_RING, 1f);
822//              weights.put(LocationType.IN_ASTEROID_FIELD, 1f);
823//              weights.put(LocationType.STAR_ORBIT, 1f);
824//              weights.put(LocationType.IN_SMALL_NEBULA, 1f);
825//              weights.put(LocationType.L_POINT, 1f);
826//              WeightedRandomPicker<EntityLocation> locs = getLocations(random, system, 100f, weights);
827//              
828//              List<AddedEntity> result = new ArrayList<AddedEntity>();
829//              for (int i = 0; i < data.numProbes; i++) {
830//                      AddedEntity e = addEntity(system, locs, Entities.DERELICT_SURVEY_PROBE, Factions.DERELICT);
831//                      if (e != null) result.add(e);
832//              }
833//              
834//              for (int i = 0; i < data.numSurveyShips; i++) {
835//                      AddedEntity e = addEntity(system, locs, Entities.DERELICT_SURVEY_SHIP, Factions.DERELICT);
836//                      if (e != null) result.add(e);
837//              }
838//              
839//              for (int i = 0; i < data.numMotherships; i++) {
840//                      AddedEntity e = addEntity(system, locs, Entities.DERELICT_MOTHERSHIP, Factions.DERELICT);
841//                      if (e != null) result.add(e);
842//              }
843//              
844//              
845//              return result;
846//
847//      }
848
849        
850        
851        @Override
852        public int getOrder() {
853                return 1000;
854        }
855
856
857        
858        
859        
860}
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876