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