001package com.fs.starfarer.api.impl.combat; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.List; 006import java.util.Random; 007 008import java.awt.Color; 009 010import org.lwjgl.util.vector.Vector2f; 011 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.BattleCreationPlugin; 014import com.fs.starfarer.api.campaign.CampaignFleetAPI; 015import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 016import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 017import com.fs.starfarer.api.campaign.LocationAPI; 018import com.fs.starfarer.api.campaign.PlanetAPI; 019import com.fs.starfarer.api.campaign.SectorEntityToken; 020import com.fs.starfarer.api.campaign.StarSystemAPI; 021import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin; 022import com.fs.starfarer.api.combat.BattleCreationContext; 023import com.fs.starfarer.api.combat.CombatEngineAPI; 024import com.fs.starfarer.api.combat.MissileAPI; 025import com.fs.starfarer.api.combat.ShipAPI; 026import com.fs.starfarer.api.fleet.FleetGoal; 027import com.fs.starfarer.api.fleet.FleetMemberAPI; 028import com.fs.starfarer.api.impl.campaign.ids.Planets; 029import com.fs.starfarer.api.impl.campaign.ids.Tags; 030import com.fs.starfarer.api.impl.campaign.ids.Terrain; 031import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceAbyssPluginImpl; 032import com.fs.starfarer.api.impl.campaign.terrain.PulsarBeamTerrainPlugin; 033import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin; 034import com.fs.starfarer.api.input.InputEventAPI; 035import com.fs.starfarer.api.mission.FleetSide; 036import com.fs.starfarer.api.mission.MissionDefinitionAPI; 037import com.fs.starfarer.api.util.Misc; 038import com.fs.starfarer.api.util.WeightedRandomPicker; 039 040public class BattleCreationPluginImpl implements BattleCreationPlugin { 041 042 public interface NebulaTextureProvider { 043 String getNebulaTex(); 044 String getNebulaMapTex(); 045 } 046 047 048 public static float ABYSS_SHIP_SPEED_PENALTY = 20f; 049 public static float ABYSS_MISSILE_SPEED_PENALTY = 20f; 050 //public static float ABYSS_MISSILE_FLIGHT_TIME_MULT = 1.25f; 051 public static float ABYSS_OVERLAY_ALPHA = 0.2f; 052 053 protected float width, height; 054 055 protected float xPad = 2000; 056 protected float yPad = 2000; 057 058 protected List<String> objs = null; 059 060 protected float prevXDir = 0; 061 protected float prevYDir = 0; 062 protected boolean escape; 063 064 protected BattleCreationContext context; 065 protected MissionDefinitionAPI loader; 066 067 public void initBattle(final BattleCreationContext context, MissionDefinitionAPI loader) { 068 069 this.context = context; 070 this.loader = loader; 071 CampaignFleetAPI playerFleet = context.getPlayerFleet(); 072 CampaignFleetAPI otherFleet = context.getOtherFleet(); 073 FleetGoal playerGoal = context.getPlayerGoal(); 074 FleetGoal enemyGoal = context.getOtherGoal(); 075 076 // doesn't work for consecutive engagements; haven't investigated why 077 //Random random = Misc.getRandom(Misc.getNameBasedSeed(otherFleet), 23); 078 079 Random random = Misc.getRandom(Misc.getSalvageSeed(otherFleet) * 080 (long)otherFleet.getFleetData().getNumMembers(), 23); 081 //System.out.println("RNG: " + random.nextLong()); 082 //random = new Random(1213123L); 083 //Random random = Misc.random; 084 085 escape = playerGoal == FleetGoal.ESCAPE || enemyGoal == FleetGoal.ESCAPE; 086 087 int maxFP = (int) Global.getSettings().getFloat("maxNoObjectiveBattleSize"); 088 int fpOne = 0; 089 int fpTwo = 0; 090 for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) { 091 if (member.canBeDeployedForCombat() || playerGoal == FleetGoal.ESCAPE) { 092 fpOne += member.getUnmodifiedDeploymentPointsCost(); 093 } 094 } 095 for (FleetMemberAPI member : otherFleet.getFleetData().getMembersListCopy()) { 096 if (member.canBeDeployedForCombat() || playerGoal == FleetGoal.ESCAPE) { 097 fpTwo += member.getUnmodifiedDeploymentPointsCost(); 098 } 099 } 100 101 int smaller = Math.min(fpOne, fpTwo); 102 103 boolean withObjectives = smaller > maxFP; 104 //withObjectives = true; 105 if (!context.objectivesAllowed) { 106 withObjectives = false; 107 } else if (context.forceObjectivesOnMap) { 108 withObjectives = true; 109 } 110 111 int numObjectives = 0; 112 if (withObjectives) { 113// if (fpOne + fpTwo > maxFP + 70) { 114// numObjectives = 3 115// numObjectives = 3 + (int)(Math.random() * 2.0); 116// } else { 117// numObjectives = 2 + (int)(Math.random() * 2.0); 118// } 119 if (fpOne + fpTwo > maxFP + 70) { 120 numObjectives = 4; 121 //numObjectives = 3 + (int)(Math.random() * 2.0); 122 } else { 123 numObjectives = 3 + random.nextInt(2); 124 //numObjectives = 2 + (int)(Math.random() * 2.0); 125 } 126 } 127 128 // shouldn't be possible, but.. 129 if (numObjectives > 4) { 130 numObjectives = 4; 131 } 132 133 int baseCommandPoints = (int) Global.getSettings().getFloat("startingCommandPoints"); 134 135 // 136 loader.initFleet(FleetSide.PLAYER, "ISS", playerGoal, false, 137 context.getPlayerCommandPoints() - baseCommandPoints, 138 (int) playerFleet.getCommanderStats().getCommandPoints().getModifiedValue() - baseCommandPoints); 139 loader.initFleet(FleetSide.ENEMY, "", enemyGoal, true, 140 (int) otherFleet.getCommanderStats().getCommandPoints().getModifiedValue() - baseCommandPoints); 141 142 List<FleetMemberAPI> playerShips = playerFleet.getFleetData().getCombatReadyMembersListCopy(); 143 if (playerGoal == FleetGoal.ESCAPE) { 144 playerShips = playerFleet.getFleetData().getMembersListCopy(); 145 } 146 for (FleetMemberAPI member : playerShips) { 147 loader.addFleetMember(FleetSide.PLAYER, member); 148 } 149 150 151 List<FleetMemberAPI> enemyShips = otherFleet.getFleetData().getCombatReadyMembersListCopy(); 152 if (enemyGoal == FleetGoal.ESCAPE) { 153 enemyShips = otherFleet.getFleetData().getMembersListCopy(); 154 } 155 for (FleetMemberAPI member : enemyShips) { 156 loader.addFleetMember(FleetSide.ENEMY, member); 157 } 158 159 width = 18000f; 160 height = 18000f; 161 162 if (escape) { 163 width = 18000f; 164 //height = 24000f; 165 height = 18000f; 166 } else if (withObjectives) { 167 width = 24000f; 168 if (numObjectives == 2) { 169 height = 14000f; 170 } else { 171 height = 18000f; 172 } 173 } 174 175 createMap(random); 176 177 context.setInitialDeploymentBurnDuration(1.5f); 178 context.setNormalDeploymentBurnDuration(6f); 179 context.setEscapeDeploymentBurnDuration(1.5f); 180 181 xPad = 2000f; 182 yPad = 3000f; 183 184 if (escape) { 185// addEscapeObjectives(loader, 4); 186// context.setInitialEscapeRange(7000f); 187// context.setFlankDeploymentDistance(9000f); 188 addEscapeObjectives(loader, 2, random); 189// context.setInitialEscapeRange(4000f); 190// context.setFlankDeploymentDistance(8000f); 191 192 context.setInitialEscapeRange(Global.getSettings().getFloat("escapeStartDistance")); 193 context.setFlankDeploymentDistance(Global.getSettings().getFloat("escapeFlankDistance")); 194 195 loader.addPlugin(new EscapeRevealPlugin(context)); 196 } else { 197 if (withObjectives) { 198 addObjectives(loader, numObjectives, random); 199 context.setStandoffRange(height - 4500f); 200 } else { 201 context.setStandoffRange(6000f); 202 } 203 204 context.setFlankDeploymentDistance(height/2f); // matters for Force Concentration 205 } 206 } 207 208 public void afterDefinitionLoad(final CombatEngineAPI engine) { 209 if (coronaIntensity > 0 && (corona != null || pulsar != null)) { 210 String name = "Corona"; 211 if (pulsar != null) name = pulsar.getTerrainName(); 212 else if (corona != null) name = corona.getTerrainName(); 213 214 final String name2 = name; 215 216// CombatFleetManagerAPI manager = engine.getFleetManager(FleetSide.PLAYER); 217// for (FleetMemberAPI member : manager.getReservesCopy()) { 218// } 219 final Object key1 = new Object(); 220 final Object key2 = new Object(); 221 final String icon = Global.getSettings().getSpriteName("ui", "icon_tactical_cr_penalty"); 222 engine.addPlugin(new BaseEveryFrameCombatPlugin() { 223 @Override 224 public void advance(float amount, List<InputEventAPI> events) { 225 engine.maintainStatusForPlayerShip(key1, icon, name2, "reduced peak time", true); 226 engine.maintainStatusForPlayerShip(key2, icon, name2, "faster CR degradation", true); 227 } 228 }); 229 } 230 231 if (abyssalDepth > 0) { 232 Color color = Misc.scaleColor(Color.white, 1f - abyssalDepth); 233 engine.setBackgroundColor(color); 234 235 color = Misc.scaleAlpha(Color.black, abyssalDepth * ABYSS_OVERLAY_ALPHA); 236 engine.setBackgroundGlowColor(color); 237 engine.setBackgroundGlowColorNonAdditive(true); 238 239 if (abyssalDepth > HyperspaceAbyssPluginImpl.DEPTH_THRESHOLD_FOR_NO_DUST_PARTICLES_IN_COMBAT) { 240 engine.setRenderStarfield(false); 241 } 242 243 final Object key1 = new Object(); 244 final Object key2 = new Object(); 245 final String icon = Global.getSettings().getSpriteName("ui", "icon_tactical_engine_damage"); 246 final String name = "Abyssal hyperspace"; 247 engine.addPlugin(new BaseEveryFrameCombatPlugin() { 248 @Override 249 public void advance(float amount, List<InputEventAPI> events) { 250 String percentSpeed = "-" + (int)Math.round(ABYSS_SHIP_SPEED_PENALTY) + "%"; 251 String percentMissile = "-" + (int)Math.round(ABYSS_MISSILE_SPEED_PENALTY) + "%"; 252 engine.maintainStatusForPlayerShip(key1, icon, name, percentSpeed + " top speed", true); 253 engine.maintainStatusForPlayerShip(key2, icon, name, percentMissile + " missle speed / range", true); 254 255 String modId = "abyssal"; 256 float modW = -0.0f * abyssalDepth; 257 float modL = -0.33f * abyssalDepth; 258 float modG = -0.5f * abyssalDepth; 259 260 for (ShipAPI curr : engine.getShips()) { 261 if (curr.isHulk()) continue; 262 263 curr.getEngineController().fadeToOtherColor(this, Color.black, null, 1f, abyssalDepth * 0.4f); 264 curr.getEngineController().extendFlame(this, modL, modW, modG); 265 266 curr.getMutableStats().getMaxSpeed().modifyMult(modId, 267 1f - abyssalDepth * ABYSS_SHIP_SPEED_PENALTY * 0.01f); 268 curr.getMutableStats().getMissileWeaponRangeBonus().modifyMult(modId, 269 1f - abyssalDepth * ABYSS_MISSILE_SPEED_PENALTY * 0.01f); 270 curr.getMutableStats().getMissileMaxSpeedBonus().modifyMult(modId, 271 1f - abyssalDepth * ABYSS_MISSILE_SPEED_PENALTY * 0.01f); 272 } 273 274 for (MissileAPI missile : engine.getMissiles()) { 275 missile.getEngineController().fadeToOtherColor(this, Color.black, null, 1f, abyssalDepth * 0.4f); 276 missile.getEngineController().extendFlame(this, modL, modW, 0f); 277 } 278 279 } 280 }); 281 282 } 283 } 284 285 286 protected float abyssalDepth = 0f; 287 protected float coronaIntensity = 0f; 288 protected StarCoronaTerrainPlugin corona = null; 289 protected PulsarBeamTerrainPlugin pulsar = null; 290 protected void createMap(Random random) { 291 loader.initMap((float)-width/2f, (float)width/2f, (float)-height/2f, (float)height/2f); 292 293 CampaignFleetAPI playerFleet = context.getPlayerFleet(); 294 String nebulaTex = null; 295 String nebulaMapTex = null; 296 boolean inNebula = false; 297 298 boolean protectedFromCorona = false; 299 for (CustomCampaignEntityAPI curr : playerFleet.getContainingLocation().getCustomEntitiesWithTag(Tags.PROTECTS_FROM_CORONA_IN_BATTLE)) { 300 if (Misc.getDistance(curr.getLocation(), playerFleet.getLocation()) <= curr.getRadius() + Global.getSector().getPlayerFleet().getRadius() + 10f) { 301 protectedFromCorona = true; 302 break; 303 } 304 } 305 306 abyssalDepth = Misc.getAbyssalDepth(playerFleet); 307 308 float numRings = 0; 309 310 Color coronaColor = null; 311 // this assumes that all nebula in a system are of the same color 312 for (CampaignTerrainAPI terrain : playerFleet.getContainingLocation().getTerrainCopy()) { 313 //if (terrain.getType().equals(Terrain.NEBULA)) { 314 if (terrain.getPlugin() instanceof NebulaTextureProvider) { 315 if (terrain.getPlugin().containsEntity(playerFleet)) { 316 inNebula = true; 317 if (terrain.getPlugin() instanceof NebulaTextureProvider) { 318 NebulaTextureProvider provider = (NebulaTextureProvider) terrain.getPlugin(); 319 nebulaTex = provider.getNebulaTex(); 320 nebulaMapTex = provider.getNebulaMapTex(); 321 } 322 } else { 323 if (nebulaTex == null) { 324 if (terrain.getPlugin() instanceof NebulaTextureProvider) { 325 NebulaTextureProvider provider = (NebulaTextureProvider) terrain.getPlugin(); 326 nebulaTex = provider.getNebulaTex(); 327 nebulaMapTex = provider.getNebulaMapTex(); 328 } 329 } 330 } 331 } else if (terrain.getPlugin() instanceof StarCoronaTerrainPlugin && pulsar == null && !protectedFromCorona) { 332 StarCoronaTerrainPlugin plugin = (StarCoronaTerrainPlugin) terrain.getPlugin(); 333 if (plugin.containsEntity(playerFleet)) { 334 float angle = Misc.getAngleInDegrees(terrain.getLocation(), playerFleet.getLocation()); 335 Color color = plugin.getAuroraColorForAngle(angle); 336 float intensity = plugin.getIntensityAtPoint(playerFleet.getLocation()); 337 intensity = 0.4f + 0.6f * intensity; 338 int alpha = (int)(80f * intensity); 339 color = Misc.setAlpha(color, alpha); 340 if (coronaColor == null || coronaColor.getAlpha() < alpha) { 341 coronaColor = color; 342 coronaIntensity = intensity; 343 corona = plugin; 344 } 345 } 346 } else if (terrain.getPlugin() instanceof PulsarBeamTerrainPlugin && !protectedFromCorona) { 347 PulsarBeamTerrainPlugin plugin = (PulsarBeamTerrainPlugin) terrain.getPlugin(); 348 if (plugin.containsEntity(playerFleet)) { 349 float angle = Misc.getAngleInDegreesStrict(terrain.getLocation(), playerFleet.getLocation()); 350 Color color = plugin.getPulsarColorForAngle(angle); 351 float intensity = plugin.getIntensityAtPoint(playerFleet.getLocation()); 352 intensity = 0.4f + 0.6f * intensity; 353 int alpha = (int)(80f * intensity); 354 color = Misc.setAlpha(color, alpha); 355 if (coronaColor == null || coronaColor.getAlpha() < alpha) { 356 coronaColor = color; 357 coronaIntensity = intensity; 358 pulsar = plugin; 359 corona = null; 360 } 361 } 362 } else if (terrain.getType().equals(Terrain.RING)) { 363 if (terrain.getPlugin().containsEntity(playerFleet)) { 364 numRings++; 365 } 366 } 367 } 368 if (nebulaTex != null) { 369 loader.setNebulaTex(nebulaTex); 370 loader.setNebulaMapTex(nebulaMapTex); 371 } 372 373 if (coronaColor != null) { 374 loader.setBackgroundGlowColor(coronaColor); 375 } 376 377 int numNebula = 15; 378 if (inNebula) { 379 numNebula = 100; 380 } 381 if (!inNebula && playerFleet.isInHyperspace()) { 382 numNebula = 0; 383 } 384 385 for (int i = 0; i < numNebula; i++) { 386 float x = random.nextFloat() * width - width/2; 387 float y = random.nextFloat() * height - height/2; 388 float radius = 100f + random.nextFloat() * 400f; 389 if (inNebula) { 390 radius += 100f + 500f * random.nextFloat(); 391 } 392 loader.addNebula(x, y, radius); 393 } 394 395 if (!playerFleet.isInHyperspace()) { 396 float numAsteroidsWithinRange = countNearbyAsteroids(playerFleet); 397 398 int numAsteroids = Math.min(400, (int)((numAsteroidsWithinRange + 1f) * 20f)); 399 400 loader.addAsteroidField(0, 0, random.nextFloat() * 360f, width, 401 20f, 70f, numAsteroids); 402 403 if (numRings > 0) { 404 int numRingAsteroids = (int) (numRings * 300 + (numRings * 600f) * random.nextFloat()); 405 //int numRingAsteroids = (int) (numRings * 1600 + (numRings * 600f) * (float) Math.random()); 406 if (numRingAsteroids > 1500) { 407 numRingAsteroids = 1500; 408 } 409 loader.addRingAsteroids(0, 0, random.nextFloat() * 360f, width, 410 100f, 200f, numRingAsteroids); 411 } 412 } 413 414 //setRandomBackground(loader); 415 loader.setBackgroundSpriteName(playerFleet.getContainingLocation().getBackgroundTextureFilename()); 416// loader.setBackgroundSpriteName("graphics/backgrounds/hyperspace_bg_cool.jpg"); 417// loader.setBackgroundSpriteName("graphics/ships/onslaught/onslaught_base.png"); 418 419 if (playerFleet.getContainingLocation() == Global.getSector().getHyperspace()) { 420 loader.setHyperspaceMode(true); 421 } else { 422 loader.setHyperspaceMode(false); 423 } 424 425 //addMultiplePlanets(); 426 addClosestPlanet(); 427 } 428 429 protected void addClosestPlanet() { 430 float bgWidth = 2048f; 431 float bgHeight = 2048f; 432 433 PlanetAPI planet = getClosestPlanet(context.getPlayerFleet()); 434 if (planet == null) return; 435 436 float dist = Vector2f.sub(context.getPlayerFleet().getLocation(), planet.getLocation(), new Vector2f()).length() - planet.getRadius(); 437 if (dist < 0) dist = 0; 438 float baseRadius = planet.getRadius(); 439 float scaleFactor = 1.5f; 440 float maxRadius = 500f; 441 float minRadius = 100f; 442 443// if (planet.isStar()) { 444// scaleFactor = 0.01f; 445// maxRadius = 20f; 446// } 447 448 float maxDist = SINGLE_PLANET_MAX_DIST - planet.getRadius(); 449 if (maxDist < 1) maxDist = 1; 450 451 452 boolean playerHasStation = false; 453 boolean enemyHasStation = false; 454 455 for (FleetMemberAPI curr : context.getPlayerFleet().getFleetData().getMembersListCopy()) { 456 if (curr.isStation()) { 457 playerHasStation = true; 458 break; 459 } 460 } 461 462 for (FleetMemberAPI curr : context.getOtherFleet().getFleetData().getMembersListCopy()) { 463 if (curr.isStation()) { 464 enemyHasStation = true; 465 break; 466 } 467 } 468 469 float planetYOffset = 0; 470 471 if (playerHasStation) { 472 planetYOffset = -bgHeight / 2f * 0.5f; 473 } 474 if (enemyHasStation) { 475 planetYOffset = bgHeight / 2f * 0.5f; 476 } 477 478 479 float f = (maxDist - dist) / maxDist * 0.65f + 0.35f; 480 float radius = baseRadius * f * scaleFactor; 481 if (radius > maxRadius) radius = maxRadius; 482 if (radius < minRadius) radius = minRadius; 483 loader.setPlanetBgSize(bgWidth * f, bgHeight * f); 484 loader.addPlanet(0f, planetYOffset, radius, planet, 0f, true); 485 } 486 487 protected void addMultiplePlanets() { 488 float bgWidth = 2048f; 489 float bgHeight = 2048f; 490 loader.setPlanetBgSize(bgWidth, bgHeight); 491 492 493 List<NearbyPlanetData> planets = getNearbyPlanets(context.getPlayerFleet()); 494 if (!planets.isEmpty()) { 495 float maxDist = PLANET_MAX_DIST; 496 for (NearbyPlanetData data : planets) { 497 float dist = Vector2f.sub(context.getPlayerFleet().getLocation(), data.planet.getLocation(), new Vector2f()).length(); 498 float baseRadius = data.planet.getRadius(); 499 float scaleFactor = 1.5f; 500 float maxRadius = 500f; 501 502 if (data.planet.isStar()) { 503 // skip stars in combat, bright and annoying 504 continue; 505// scaleFactor = 0.1f; 506// maxRadius = 50f; 507 } 508 509 float f = (maxDist - dist) / maxDist * 0.65f + 0.35f; 510 float radius = baseRadius * f * scaleFactor; 511 if (radius > maxRadius) radius = maxRadius; 512 513 loader.addPlanet(data.offset.x * bgWidth / PLANET_AREA_WIDTH * scaleFactor, 514 data.offset.y * bgHeight / PLANET_AREA_HEIGHT * scaleFactor, 515 radius, data.planet.getTypeId(), 0f, true); 516 } 517 518 } 519 } 520 521 522 protected void setRandomBackground(MissionDefinitionAPI loader, Random random) { 523 // these have to be loaded using the graphics section in settings.json 524 String [] bgs = new String [] { 525 "graphics/backgrounds/background1.jpg", 526 "graphics/backgrounds/background2.jpg", 527 "graphics/backgrounds/background3.jpg", 528 "graphics/backgrounds/background4.jpg" 529 }; 530 String pick = bgs[Math.min(bgs.length - 1, (int)(random.nextDouble() * bgs.length))]; 531 loader.setBackgroundSpriteName(pick); 532 } 533 534 protected static String COMM = "comm_relay"; 535 protected static String SENSOR = "sensor_array"; 536 protected static String NAV = "nav_buoy"; 537 538 protected void addObjectives(MissionDefinitionAPI loader, int num, Random random) { 539 //if (true) return; 540 541 objs = new ArrayList<String>(Arrays.asList(new String [] { 542 SENSOR, 543 SENSOR, 544 NAV, 545 NAV, 546 //COMM, 547 //COMM, 548 })); 549 550 if (num == 2) { // minimum is 3 now, so this shouldn't happen 551 objs = new ArrayList<String>(Arrays.asList(new String [] { 552 SENSOR, 553 SENSOR, 554 NAV, 555 NAV, 556 COMM, 557 })); 558 addObjectiveAt(0.25f, 0.5f, 0f, 0f, random); 559 addObjectiveAt(0.75f, 0.5f, 0f, 0f, random); 560 } else if (num == 3) { 561 float r = random.nextFloat(); 562 if (r < 0.33f) { 563 addObjectiveAt(0.25f, 0.7f, 1f, 1f, random); 564 addObjectiveAt(0.25f, 0.3f, 1f, 1f, random); 565 addObjectiveAt(0.75f, 0.5f, 1f, 1f, COMM, random); 566 } else if (r < 0.67f) { 567 addObjectiveAt(0.75f, 0.7f, 1f, 1f, random); 568 addObjectiveAt(0.75f, 0.3f, 1f, 1f, random); 569 addObjectiveAt(0.25f, 0.5f, 1f, 1f, COMM, random); 570 } else { 571 if (random.nextFloat() < 0.5f) { 572 addObjectiveAt(0.22f, 0.7f, 1f, 1f, random); 573 addObjectiveAt(0.5f, 0.5f, 1f, 1f, COMM, random); 574 addObjectiveAt(0.78f, 0.3f, 1f, 1f, random); 575 } else { 576 addObjectiveAt(0.22f, 0.3f, 1f, 1f, random); 577 addObjectiveAt(0.5f, 0.5f, 1f, 1f, COMM, random); 578 addObjectiveAt(0.78f, 0.7f, 1f, 1f, random); 579 } 580 } 581 } else if (num == 4) { 582 float r = random.nextFloat(); 583 if (r < 0.33f) { 584 String [] maybeRelays = pickCommRelays(2, 2, false, true, true, false, random); 585 addObjectiveAt(0.25f, 0.25f, 2f, 1f, maybeRelays[0], random); 586 addObjectiveAt(0.25f, 0.75f, 2f, 1f, maybeRelays[1], random); 587 addObjectiveAt(0.75f, 0.25f, 2f, 1f, maybeRelays[2], random); 588 addObjectiveAt(0.75f, 0.75f, 2f, 1f, maybeRelays[3], random); 589 } else if (r < 0.67f) { 590 String [] maybeRelays = pickCommRelays(1, 2, true, false, true, false, random); 591 addObjectiveAt(0.25f, 0.5f, 1f, 1f, maybeRelays[0], random); 592 addObjectiveAt(0.5f, 0.75f, 1f, 1f, maybeRelays[1], random); 593 addObjectiveAt(0.75f, 0.5f, 1f, 1f, maybeRelays[2], random); 594 addObjectiveAt(0.5f, 0.25f, 1f, 1f, maybeRelays[3], random); 595 } else { 596 if (random.nextFloat() < 0.5f) { 597 String [] maybeRelays = pickCommRelays(1, 2, true, false, true, false, random); 598 addObjectiveAt(0.25f, 0.25f, 1f, 0f, maybeRelays[0], random); 599 addObjectiveAt(0.4f, 0.6f, 1f, 0f, maybeRelays[1], random); 600 addObjectiveAt(0.6f, 0.4f, 1f, 0f, maybeRelays[2], random); 601 addObjectiveAt(0.75f, 0.75f, 1f, 0f, maybeRelays[3], random); 602 } else { 603 String [] maybeRelays = pickCommRelays(1, 2, false, true, false, true, random); 604 addObjectiveAt(0.25f, 0.75f, 1f, 0f, maybeRelays[0], random); 605 addObjectiveAt(0.4f, 0.4f, 1f, 0f, maybeRelays[1], random); 606 addObjectiveAt(0.6f, 0.6f, 1f, 0f, maybeRelays[2], random); 607 addObjectiveAt(0.75f, 0.25f, 1f, 0f, maybeRelays[3], random); 608 } 609 } 610 } 611 } 612 613 protected String [] pickCommRelays(int min, int max, boolean comm1, boolean comm2, boolean comm3, boolean comm4, Random random) { 614 String [] result = new String [4]; 615 616 WeightedRandomPicker<Integer> picker = new WeightedRandomPicker<Integer>(random); 617 if (comm1) picker.add(0); 618 if (comm2) picker.add(1); 619 if (comm3) picker.add(2); 620 if (comm4) picker.add(3); 621 622 int num = min + random.nextInt(max - min + 1); 623 624 for (int i = 0; i < num && !picker.isEmpty(); i++) { 625 result[picker.pickAndRemove()] = COMM; 626 } 627 return result; 628 } 629 630 631 protected void addEscapeObjectives(MissionDefinitionAPI loader, int num, Random random) { 632 objs = new ArrayList<String>(Arrays.asList(new String [] { 633 SENSOR, 634 SENSOR, 635 NAV, 636 NAV, 637 COMM, 638 })); 639 640 if (num == 2) { 641 float r = random.nextFloat(); 642 if (r < 0.33f) { 643 addObjectiveAt(0.25f, 0.25f, 1f, 1f, random); 644 addObjectiveAt(0.75f, 0.75f, 1f, 1f, random); 645 } else if (r < 0.67f) { 646 addObjectiveAt(0.75f, 0.25f, 1f, 1f, random); 647 addObjectiveAt(0.25f, 0.75f, 1f, 1f, random); 648 } else { 649 addObjectiveAt(0.5f, 0.25f, 4f, 2f, random); 650 addObjectiveAt(0.5f, 0.75f, 4f, 2f, random); 651 } 652 } else if (num == 3) { 653 float r = random.nextFloat(); 654 if (r < 0.33f) { 655 addObjectiveAt(0.25f, 0.75f, 1f, 1f, random); 656 addObjectiveAt(0.25f, 0.25f, 1f, 1f, random); 657 addObjectiveAt(0.75f, 0.5f, 1f, 6f, random); 658 } else if (r < 0.67f) { 659 addObjectiveAt(0.25f, 0.5f, 1f, 6f, random); 660 addObjectiveAt(0.75f, 0.75f, 1f, 1f, random); 661 addObjectiveAt(0.75f, 0.25f, 1f, 1f, random); 662 } else { 663 addObjectiveAt(0.5f, 0.25f, 4f, 1f, random); 664 addObjectiveAt(0.5f, 0.5f, 4f, 1f, random); 665 addObjectiveAt(0.5f, 0.75f, 4f, 1f, random); 666 } 667 } else if (num == 4) { 668 float r = random.nextFloat(); 669 if (r < 0.33f) { 670 addObjectiveAt(0.25f, 0.25f, 1f, 1f, random); 671 addObjectiveAt(0.25f, 0.75f, 1f, 1f, random); 672 addObjectiveAt(0.75f, 0.25f, 1f, 1f, random); 673 addObjectiveAt(0.75f, 0.75f, 1f, 1f, random); 674 } else if (r < 0.67f) { 675 addObjectiveAt(0.35f, 0.25f, 2f, 0f, random); 676 addObjectiveAt(0.65f, 0.35f, 2f, 0f, random); 677 addObjectiveAt(0.5f, 0.6f, 4f, 1f, random); 678 addObjectiveAt(0.5f, 0.8f, 4f, 1f, random); 679 } else { 680 addObjectiveAt(0.65f, 0.25f, 2f, 0f, random); 681 addObjectiveAt(0.35f, 0.35f, 2f, 0f, random); 682 addObjectiveAt(0.5f, 0.6f, 4f, 1f, random); 683 addObjectiveAt(0.5f, 0.8f, 4f, 1f, random); 684 } 685 } 686 } 687 688 protected void addObjectiveAt(float xMult, float yMult, float xOff, float yOff, Random random) { 689 addObjectiveAt(xMult, yMult, xOff, yOff, null, random); 690 } 691 protected void addObjectiveAt(float xMult, float yMult, float xOff, float yOff, String type, Random random) { 692 //String type = pickAny(); 693 if (type == null) { 694 type = pickAny(random); 695 if (objs != null && objs.size() > 0) { 696 int index = (int) (random.nextDouble() * objs.size()); 697 type = objs.remove(index); 698 } 699 } 700 701 float minX = -width/2 + xPad; 702 float minY = -height/2 + yPad; 703 704 float x = (width - xPad * 2f) * xMult + minX; 705 float y = (height - yPad * 2f) * yMult + minY; 706 707 x = ((int) x / 1000) * 1000f; 708 y = ((int) y / 1000) * 1000f; 709 710 float offsetX = Math.round((random.nextFloat() - 0.5f) * xOff * 1f) * 1000f; 711 float offsetY = Math.round((random.nextFloat() - 0.5f) * yOff * 1f) * 1000f; 712 713// offsetX = 0; 714// offsetY = 0; 715 716 float xDir = (float) Math.signum(offsetX); 717 float yDir = (float) Math.signum(offsetY); 718 719 if (xDir == prevXDir && xOff > 0) { 720 xDir = -xDir; 721 offsetX = Math.abs(offsetX) * -prevXDir; 722 } 723 724 if (yDir == prevYDir && yOff > 0) { 725 yDir = -yDir; 726 offsetY = Math.abs(offsetY) * -prevYDir; 727 } 728 729 prevXDir = xDir; 730 prevYDir = yDir; 731 732 x += offsetX; 733 y += offsetY; 734 735 loader.addObjective(x, y, type); 736 737 if (random.nextFloat() > 0.6f && loader.hasNebula()) { 738 float nebulaSize = random.nextFloat() * 1500f + 500f; 739 loader.addNebula(x, y, nebulaSize); 740 } 741 } 742 743 protected String pickAny(Random random) { 744 float r = random.nextFloat(); 745 if (r < 0.33f) return "nav_buoy"; 746 else if (r < 0.67f) return "sensor_array"; 747 else return "comm_relay"; 748 } 749 750 protected float countNearbyAsteroids(CampaignFleetAPI playerFleet) { 751 float numAsteroidsWithinRange = 0; 752 LocationAPI loc = playerFleet.getContainingLocation(); 753 if (loc instanceof StarSystemAPI) { 754 StarSystemAPI system = (StarSystemAPI) loc; 755 List<SectorEntityToken> asteroids = system.getAsteroids(); 756 for (SectorEntityToken asteroid : asteroids) { 757 float range = Vector2f.sub(playerFleet.getLocation(), asteroid.getLocation(), new Vector2f()).length(); 758 if (range < 300) numAsteroidsWithinRange ++; 759 } 760 } 761 return numAsteroidsWithinRange; 762 } 763 764 protected static class NearbyPlanetData { 765 protected Vector2f offset; 766 protected PlanetAPI planet; 767 public NearbyPlanetData(Vector2f offset, PlanetAPI planet) { 768 this.offset = offset; 769 this.planet = planet; 770 } 771 } 772 773 protected static float PLANET_AREA_WIDTH = 2000; 774 protected static float PLANET_AREA_HEIGHT = 2000; 775 protected static float PLANET_MAX_DIST = (float) Math.sqrt(PLANET_AREA_WIDTH/2f * PLANET_AREA_WIDTH/2f + PLANET_AREA_HEIGHT/2f * PLANET_AREA_WIDTH/2f); 776 777 protected static float SINGLE_PLANET_MAX_DIST = 1000f; 778 779 protected List<NearbyPlanetData> getNearbyPlanets(CampaignFleetAPI playerFleet) { 780 LocationAPI loc = playerFleet.getContainingLocation(); 781 List<NearbyPlanetData> result = new ArrayList<NearbyPlanetData>(); 782 if (loc instanceof StarSystemAPI) { 783 StarSystemAPI system = (StarSystemAPI) loc; 784 List<PlanetAPI> planets = system.getPlanets(); 785 for (PlanetAPI planet : planets) { 786 float diffX = planet.getLocation().x - playerFleet.getLocation().x; 787 float diffY = planet.getLocation().y - playerFleet.getLocation().y; 788 789 if (Math.abs(diffX) < PLANET_AREA_WIDTH/2f && Math.abs(diffY) < PLANET_AREA_HEIGHT/2f) { 790 result.add(new NearbyPlanetData(new Vector2f(diffX, diffY), planet)); 791 } 792 } 793 } 794 return result; 795 } 796 797 protected PlanetAPI getClosestPlanet(CampaignFleetAPI playerFleet) { 798 LocationAPI loc = playerFleet.getContainingLocation(); 799 PlanetAPI closest = null; 800 float minDist = Float.MAX_VALUE; 801 if (loc instanceof StarSystemAPI) { 802 StarSystemAPI system = (StarSystemAPI) loc; 803 List<PlanetAPI> planets = system.getPlanets(); 804 for (PlanetAPI planet : planets) { 805 if (planet.isStar()) continue; 806 if (Planets.PLANET_LAVA.equals(planet.getTypeId())) continue; 807 if (Planets.PLANET_LAVA_MINOR.equals(planet.getTypeId())) continue; 808 if (planet.getSpec().isDoNotShowInCombat()) continue; 809 810 float dist = Vector2f.sub(context.getPlayerFleet().getLocation(), planet.getLocation(), new Vector2f()).length(); 811 if (dist < minDist && dist < SINGLE_PLANET_MAX_DIST) { 812 closest = planet; 813 minDist = dist; 814 } 815 } 816 } 817 return closest; 818 } 819} 820 821 822 823