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