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.List;
007
008import org.lwjgl.util.vector.Vector2f;
009
010import com.fs.starfarer.api.campaign.PlanetAPI;
011import com.fs.starfarer.api.campaign.StarSystemAPI;
012import com.fs.starfarer.api.impl.campaign.ids.Entities;
013import com.fs.starfarer.api.impl.campaign.ids.Tags;
014import com.fs.starfarer.api.impl.campaign.procgen.Constellation;
015import com.fs.starfarer.api.impl.campaign.procgen.NameAssigner;
016import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
017import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner.SpecialCreationContext;
018import com.fs.starfarer.api.util.Misc;
019import com.fs.starfarer.api.util.WeightedRandomPicker;
020
021
022public class RuinsThemeGenerator extends BaseThemeGenerator {
023
024        public static final int MIN_CONSTELLATIONS_WITH_RUINS = 15;
025        public static final int MAX_CONSTELLATIONS_WITH_RUINS = 25;
026        
027        public static float CONSTELLATION_SKIP_PROB = 0f;
028        
029        
030        public String getThemeId() {
031                return Themes.RUINS;
032        }
033
034        
035        @Override
036        public void generateForSector(ThemeGenContext context, float allowedUnusedFraction) {
037                
038                float total = (float) (context.constellations.size() - context.majorThemes.size()) * allowedUnusedFraction;
039                if (total <= 0) return;
040                
041                int num = (int) StarSystemGenerator.getNormalRandom(MIN_CONSTELLATIONS_WITH_RUINS, MAX_CONSTELLATIONS_WITH_RUINS);
042                if (num > total) num = (int) total;
043                
044                
045                List<Constellation> constellations = getSortedAvailableConstellations(context, false, new Vector2f(), null);
046                Collections.reverse(constellations);
047                
048                float skipProb = CONSTELLATION_SKIP_PROB;
049                if (total < num / (1f - skipProb)) {
050                        skipProb = 1f - (num / total);
051                }
052                skipProb = 0f;
053
054                List<StarSystemData> ruinSystems = new ArrayList<StarSystemData>();
055                
056                if (DEBUG) System.out.println("\n\n\n");
057                if (DEBUG) System.out.println("Generating systems with ruins");
058                
059                int count = 0;
060                
061                int numUsed = 0;
062                for (int i = 0; i < num && i < constellations.size(); i++) {
063                        Constellation c = constellations.get(i);
064                        if (random.nextFloat() < skipProb) {
065                                if (DEBUG) System.out.println("Skipping constellation " + c.getName());
066                                continue;
067                        }
068                        
069                        
070//                      if (c.getName().toLowerCase().contains("shero")) {
071//                              System.out.println("wefwefwef");
072//                      }
073                        
074                        List<StarSystemData> systems = new ArrayList<StarSystemData>();
075                        for (StarSystemAPI system : c.getSystems()) {
076                                StarSystemData data = computeSystemData(system);
077                                systems.add(data);
078                        }
079                        
080                        List<StarSystemData> mainCandidates = getSortedSystemsSuitedToBePopulated(systems);
081                        
082                        int numMain = 1 + random.nextInt(3);
083                        if (numMain > mainCandidates.size()) numMain = mainCandidates.size();
084                        if (numMain <= 0) {
085                                if (DEBUG) System.out.println("Skipping constellation " + c.getName() + ", no suitable main candidates");
086                                continue;
087                        }
088                        
089                        context.majorThemes.put(c, Themes.RUINS);
090                        numUsed++;
091
092                        if (DEBUG) System.out.println("Generating " + numMain + " main systems in " + c.getName());
093                        for (int j = 0; j < numMain; j++) {
094                                StarSystemData data = mainCandidates.get(j);
095                                populateMain(data);
096                                
097                                data.system.addTag(Tags.THEME_INTERESTING);
098                                data.system.addTag(Tags.THEME_RUINS);
099                                data.system.addTag(Tags.THEME_RUINS_MAIN);
100                                ruinSystems.add(data);
101
102                                RuinsFleetRouteManager fleets = new RuinsFleetRouteManager(data.system);
103                                data.system.addScript(fleets);
104                                
105                                if (!NameAssigner.isNameSpecial(data.system)) {
106                                        NameAssigner.assignSpecialNames(data.system);
107                                }
108                        }
109                        
110                        for (StarSystemData data : systems) {
111                                int index = mainCandidates.indexOf(data);
112                                if (index >= 0 && index < numMain) continue;
113                                
114                                populateNonMain(data);
115                                
116                                data.system.addTag(Tags.THEME_INTERESTING);
117                                data.system.addTag(Tags.THEME_RUINS);
118                                data.system.addTag(Tags.THEME_RUINS_SECONDARY);
119                                ruinSystems.add(data);
120                        }
121                        
122//                      if (count == 1) {
123//                              System.out.println("RANDOM INDEX " + count + ": " + random.nextLong());
124//                      }
125                        count++;
126                }
127                
128                SpecialCreationContext specialContext = new SpecialCreationContext();
129                specialContext.themeId = getThemeId();
130                SalvageSpecialAssigner.assignSpecials(ruinSystems, specialContext);
131                
132                if (DEBUG) System.out.println("Finished generating systems with ruins\n\n\n\n\n");
133                
134        }
135        
136
137        
138        public void populateNonMain(StarSystemData data) {
139                if (DEBUG) System.out.println(" Generating secondary ruins in " + data.system.getName());
140                boolean special = data.isBlackHole() || data.isNebula() || data.isPulsar();
141                if (special) {
142                        addResearchStations(data, 0.75f, 1, 1, createStringPicker(Entities.STATION_RESEARCH, 10f));
143                }
144                
145                if (random.nextFloat() < 0.5f) return;
146                
147                if (!data.resourceRich.isEmpty()) {
148                        addMiningStations(data, 0.5f, 1, 1, createStringPicker(Entities.STATION_MINING, 10f));
149                }
150                
151                if (!special && !data.habitable.isEmpty()) {
152                        // ruins on planet, or orbital station
153                        addHabCenters(data, 0.25f, 1, 1, createStringPicker(Entities.ORBITAL_HABITAT, 10f));
154                }
155                
156                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(random, data.system.getCenter(),
157                                                                                                                                                        15f, 10f, 10f);
158                
159                addShipGraveyard(data, 0.05f, 1, 1, factions);
160                
161                addDebrisFields(data, 0.25f, 1, 2);
162
163                addDerelictShips(data, 0.5f, 0, 3, factions);
164                
165                addCaches(data, 0.25f, 0, 2, createStringPicker( 
166                                Entities.WEAPONS_CACHE, 4f,
167                                Entities.WEAPONS_CACHE_SMALL, 10f,
168                                Entities.WEAPONS_CACHE_HIGH, 4f,
169                                Entities.WEAPONS_CACHE_SMALL_HIGH, 10f,
170                                Entities.WEAPONS_CACHE_LOW, 4f,
171                                Entities.WEAPONS_CACHE_SMALL_LOW, 10f,
172                                Entities.SUPPLY_CACHE, 4f,
173                                Entities.SUPPLY_CACHE_SMALL, 10f,
174                                Entities.EQUIPMENT_CACHE, 4f,
175                                Entities.EQUIPMENT_CACHE_SMALL, 10f
176                                ));
177                
178        }
179        
180        
181        public void populateMain(StarSystemData data) {
182                
183                if (DEBUG) System.out.println(" Generating ruins in " + data.system.getName());
184                
185                StarSystemAPI system = data.system;
186                
187                int maxHabCenters = 1 + random.nextInt(3);
188                
189                HabitationLevel level = HabitationLevel.LOW;
190                if (maxHabCenters == 2) level = HabitationLevel.MEDIUM;
191                if (maxHabCenters >= 3) level = HabitationLevel.HIGH;
192
193                addHabCenters(data, 1, maxHabCenters, maxHabCenters, createStringPicker(Entities.ORBITAL_HABITAT, 10f));
194                
195                // add various stations, orbiting entities, etc
196                float probGate = 1f;
197                float probRelay = 1f;
198                float probMining = 0.5f;
199                float probResearch = 0.25f;
200                
201                switch (level) {
202                case HIGH:
203                        probGate = 0.5f;
204                        probRelay = 1f;
205                        break;
206                case MEDIUM:
207                        probGate = 0.3f;
208                        probRelay = 0.75f;
209                        break;
210                case LOW:
211                        probGate = 0.2f;
212                        probRelay = 0.5f;
213                        break;
214                }
215                
216//              MN-6186477243757813340          
217//              float test = Misc.getDistance(data.system.getLocation(), new Vector2f(48500, -22000));
218//              if (test < 1000) {
219//                      System.out.println("HERE: " + random.nextLong());
220//              }
221//              if (data.system.getName().toLowerCase().contains("cadmus")) {
222//                      System.out.println("wefwefwefew");
223//              }
224                //addCommRelay(data, probRelay);
225                
226                addObjectives(data, probRelay);
227                
228                
229                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(random, system.getCenter(),
230                                                                                                15f, 5f, 5f);
231                addInactiveGate(data, probGate, 0.75f, 0.75f, factions);
232                
233                addShipGraveyard(data, 0.25f, 1, 1, factions);
234                
235                addMiningStations(data, probMining, 1, 1, createStringPicker(Entities.STATION_MINING, 10f));
236                
237                addResearchStations(data, probResearch, 1, 1, createStringPicker(Entities.STATION_RESEARCH, 10f));
238                
239                
240                //addDebrisFields(data, 0.75f, 1, 5, Factions.REMNANTS, 0.2f, 1, 3);
241                addDebrisFields(data, 0.75f, 1, 5);
242
243                addDerelictShips(data, 0.75f, 0, 7, factions);
244                
245                
246                WeightedRandomPicker<String> caches = createStringPicker( 
247                                Entities.SUPPLY_CACHE, 10f,
248                                Entities.SUPPLY_CACHE_SMALL, 10f,
249                                Entities.EQUIPMENT_CACHE, 10f,
250                                Entities.EQUIPMENT_CACHE_SMALL, 10f
251                                );
252                
253                
254                float r = random.nextFloat();
255                if (r < 0.33f) {
256                        caches.add(Entities.WEAPONS_CACHE, 10f);
257                        caches.add(Entities.WEAPONS_CACHE_SMALL, 10f);
258                } else if (r < 0.67f) {
259                        caches.add(Entities.WEAPONS_CACHE_LOW, 10f);
260                        caches.add(Entities.WEAPONS_CACHE_SMALL_LOW, 10f);
261                } else {
262                        caches.add(Entities.WEAPONS_CACHE_HIGH, 10f);
263                        caches.add(Entities.WEAPONS_CACHE_SMALL_HIGH, 10f);
264                }
265                
266                addCaches(data, 0.75f, 0, 3, caches);
267                
268        }
269        
270        
271        
272        public List<StarSystemData> getSortedSystemsSuitedToBePopulated(List<StarSystemData> systems) {
273                List<StarSystemData> result = new ArrayList<StarSystemData>();
274                
275                for (StarSystemData data : systems) {
276                        if (data.isBlackHole() || data.isNebula() || data.isPulsar()) continue;
277                        
278                        if (data.planets.size() >= 4 || data.habitable.size() >= 1) {
279                                result.add(data);
280                        }
281                }
282                
283                Collections.sort(systems, new Comparator<StarSystemData>() {
284                        public int compare(StarSystemData o1, StarSystemData o2) {
285                                float s1 = getMainCenterScore(o1);
286                                float s2 = getMainCenterScore(o2);
287                                return (int) Math.signum(s2 - s1);
288                        }
289                });
290                
291                return result;
292        }
293        
294        public float getMainCenterScore(StarSystemData data) {
295                float total = 0f;
296                total += data.planets.size() * 1f;
297                total += data.habitable.size() * 2f;
298                total += data.resourceRich.size() * 0.25f;
299                return total;
300        }
301        
302        
303        
304        /**
305         * Sorted by *descending* distance from sortFrom.
306         * @param context
307         * @param sortFrom
308         * @return
309         */
310        protected List<Constellation> getSortedAvailableConstellations(ThemeGenContext context, boolean emptyOk, final Vector2f sortFrom, List<Constellation> exclude) {
311                List<Constellation> constellations = new ArrayList<Constellation>();
312                for (Constellation c : context.constellations) {
313                        if (context.majorThemes.containsKey(c)) continue;
314                        if (!emptyOk && constellationIsEmpty(c)) continue;
315                        
316                        constellations.add(c);
317                }
318                
319                if (exclude != null) {
320                        constellations.removeAll(exclude);
321                }
322                
323                Collections.sort(constellations, new Comparator<Constellation>() {
324                        public int compare(Constellation o1, Constellation o2) {
325                                float d1 = Misc.getDistance(o1.getLocation(), sortFrom);
326                                float d2 = Misc.getDistance(o2.getLocation(), sortFrom);
327                                return (int) Math.signum(d2 - d1);
328                        }
329                });
330                return constellations;
331        }
332        
333        
334        public static boolean constellationIsEmpty(Constellation c) {
335                for (StarSystemAPI s : c.getSystems()) {
336                        if (!systemIsEmpty(s)) return false;
337                }
338                return true;
339        }
340        public static boolean systemIsEmpty(StarSystemAPI system) {
341                for (PlanetAPI p : system.getPlanets()) {
342                        if (!p.isStar()) return false;
343                }
344                return true;
345        }
346        
347        
348        
349        
350        @Override
351        public int getOrder() {
352                return 2000;
353        }
354
355
356        
357        
358}
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374