001package com.fs.starfarer.api.impl.campaign.terrain; 002 003import java.util.ArrayList; 004import java.util.EnumSet; 005import java.util.List; 006import java.util.Random; 007 008import java.awt.Color; 009 010import org.lwjgl.opengl.GL11; 011import org.lwjgl.util.vector.Vector2f; 012 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.CampaignEngineLayers; 015import com.fs.starfarer.api.campaign.CampaignFleetAPI; 016import com.fs.starfarer.api.campaign.SectorEntityToken; 017import com.fs.starfarer.api.campaign.StarSystemAPI; 018import com.fs.starfarer.api.campaign.TerrainAIFlags; 019import com.fs.starfarer.api.campaign.rules.MemoryAPI; 020import com.fs.starfarer.api.combat.ViewportAPI; 021import com.fs.starfarer.api.fleet.FleetMemberAPI; 022import com.fs.starfarer.api.fleet.FleetMemberViewAPI; 023import com.fs.starfarer.api.graphics.SpriteAPI; 024import com.fs.starfarer.api.impl.campaign.abilities.EmergencyBurnAbility; 025import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 026import com.fs.starfarer.api.impl.campaign.ids.Stats; 027import com.fs.starfarer.api.impl.campaign.ids.Tags; 028import com.fs.starfarer.api.impl.combat.BattleCreationPluginImpl; 029import com.fs.starfarer.api.loading.Description.Type; 030import com.fs.starfarer.api.ui.Alignment; 031import com.fs.starfarer.api.ui.TooltipMakerAPI; 032import com.fs.starfarer.api.util.FlickerUtilV2; 033import com.fs.starfarer.api.util.Misc; 034import com.fs.starfarer.api.util.WeightedRandomPicker; 035 036public class HyperspaceTerrainPlugin extends BaseTiledTerrain { // implements NebulaTextureProvider { 037 038 public static float ABYSS_MUSIC_SUPPRESSION = 1f; 039 040// public static class AbyssalMusicDamper implements EveryFrameScript { 041// public boolean isDone() { 042// return false; 043// } 044// public boolean runWhilePaused() { 045// return true; 046// } 047// 048// public void advance(float amount) { 049// 050// } 051// } 052 053 054 public static float ABYSS_VISIBLITY_MULT = 0.25f; 055 public static float ABYSS_SENSOR_RANGE_MULT = 0.25f; 056 public static float ABYSS_BURN_MULT = 0.25f; 057 058 public static float ABYSS_NAVIGATION_EFFECT = 0; 059 060 061 public static Color ABYSS_BACKGROUND_COLOR = new Color(0, 0, 0, 255); 062 public static Color ABYSS_PARTICLE_COLOR = new Color(0, 0, 0, 0); 063 public static Color ABYSS_LIGHT_COLOR = new Color(170, 170, 170, 255); 064 065 066 067 public static String STORM_STRIKE_TIMEOUT_KEY = "$stormStrikeTimeout"; 068 069 public static float VISIBLITY_MULT = 0.5f; 070 071 072 public static float STORM_STRIKE_SOUND_RANGE = 1500f; 073 074 public static float STORM_MIN_TIMEOUT = 0.4f; 075 public static float STORM_MAX_TIMEOUT = 0.6f; 076 public static float STORM_DAMAGE_FRACTION = 0.3f; 077 public static float STORM_MIN_STRIKE_DAMAGE = 0.05f; 078 public static float STORM_MAX_STRIKE_DAMAGE = 0.95f; 079 080 public static float STORM_SPEED_MULT = 1f; 081 public static float STORM_SENSOR_RANGE_MULT = 1f; 082 public static float STORM_VISIBILITY_FLAT = 0f; 083 084 085 public static float TILE_SIZE = 200; 086 087 public static final CampaignEngineLayers FLASH = CampaignEngineLayers.TERRAIN_6A; 088 public static final CampaignEngineLayers FLASH_OVER = CampaignEngineLayers.TERRAIN_9; 089 public static final CampaignEngineLayers GLOW = CampaignEngineLayers.TERRAIN_8; 090 public static final CampaignEngineLayers BASE = CampaignEngineLayers.TERRAIN_6; 091 //public static final CampaignEngineLayers OVER = CampaignEngineLayers.TERRAIN_6B; 092 public static final CampaignEngineLayers SHIVER = CampaignEngineLayers.TERRAIN_9; 093 public static final CampaignEngineLayers BASE_OVER = CampaignEngineLayers.TERRAIN_7; 094 095 public static enum LocationState { 096 OPEN, 097 DEEP, 098 DEEP_STORM, 099 } 100 101 public static enum CellState { 102 OFF, 103 WAIT, 104 SIGNAL, 105 STORM, 106 STORM_WANE, 107 } 108 public static class CellStateTracker { 109 public int i, j; 110 public CellState state; 111 public float wait, signal, wane; 112 private float maxSignal, maxWane; 113 public FlickerUtilV2 flicker = null; 114 public CellStateTracker(int i, int j, float wait, float signal) { 115 this.i = i; 116 this.j = j; 117 this.wait = wait; 118 this.signal = signal; 119 this.maxSignal = signal; 120 state = CellState.WAIT; 121 } 122 123 124 public void advance(float days) { 125 if (state == CellState.OFF) return; 126 127 if (state == CellState.WAIT && days > 0) { 128 wait -= days; 129 if (wait <= 0) { 130 days = -wait; 131 wait = 0; 132 state = CellState.SIGNAL; 133 } 134 } 135 136 if (state == CellState.SIGNAL && days > 0) { 137 signal -= days; 138 if (signal <= 0) { 139 days = -signal; 140 signal = 0; 141 state = CellState.STORM; 142 flicker = new FlickerUtilV2(); 143 flicker.newBurst(); 144 } 145 } 146 147 if (state == CellState.STORM || state == CellState.STORM_WANE) { 148 signal -= days; // needed for signal brightness to fade 149 } 150 151 if (state == CellState.STORM_WANE && days > 0) { 152 wane -= days; 153 if (wane <= 0) { 154 days = -wane; 155 wane = 0; 156 if (flicker == null || flicker.getBrightness() <= 0) { 157 state = CellState.OFF; 158 } else { 159 flicker.stop(); 160 } 161 } 162 } 163 164 if (flicker != null) { 165 flicker.advance(days * 7f); 166 } 167 } 168 169 public float getSignalBrightness() { 170 if (state == CellState.SIGNAL) { 171 return 1f - signal / maxSignal; 172 } 173// if (state == CellState.STORM || state == CellState.STORM_WANE) { 174// //System.out.println(Math.max(0, 1 + signal * 10)); 175// return Math.max(0, 1 + signal * 10); // fade out over 1 second, signal is negative here 176// } 177 if (state == CellState.STORM) { 178 return 1f; 179 } 180 if (state == CellState.STORM_WANE) { 181 float fade = maxWane * 0.25f; 182 if (wane > fade) { 183 return 1f; 184 } 185 return Math.max(0, wane / fade); 186 } 187 return 0f; 188 } 189 190 public void wane(float wane) { 191 this.wane = wane; 192 this.maxWane = wane; 193 state = CellState.STORM_WANE; 194 } 195 196 public boolean isOff() { 197 return state == CellState.OFF; 198 } 199 200 public boolean isStorming() { 201 return state == CellState.STORM || state == CellState.STORM_WANE; 202 } 203 204 public boolean isWaning() { 205 return state == CellState.STORM_WANE; 206 } 207 208 public boolean isSignaling() { 209 return state == CellState.SIGNAL; 210 } 211 } 212 213 protected transient SpriteAPI flickerTexture; 214 215 protected transient CellStateTracker [][] activeCells; 216 protected List<CellStateTracker> savedActiveCells = new ArrayList<CellStateTracker>(); 217 218 protected HyperspaceAutomaton auto; 219 220 protected transient String stormSoundId = null; 221 protected HyperspaceAbyssPlugin abyssPlugin; 222 223 protected SectorEntityToken abyssDarkSource = null; 224 225 public void init(String terrainId, SectorEntityToken entity, Object param) { 226 super.init(terrainId, entity, param); 227 } 228 229 public HyperspaceAbyssPlugin getAbyssPlugin() { 230 if (abyssPlugin == null) { 231 abyssPlugin = new HyperspaceAbyssPluginImpl(); 232 } 233 return abyssPlugin; 234 } 235 236 public void setAbyssPlugin(HyperspaceAbyssPlugin abyssChecker) { 237 this.abyssPlugin = abyssChecker; 238 } 239 240 protected Object readResolve() { 241 super.readResolve(); 242 layers = EnumSet.of(BASE, FLASH, GLOW, SHIVER, BASE_OVER, FLASH_OVER); 243 244 if (abyssPlugin == null) { 245 abyssPlugin = new HyperspaceAbyssPluginImpl(); 246 } 247 248 if (auto == null) { 249 //auto = new HyperspaceAutomaton(params.w, params.h, 0.75f, 1.25f); 250 auto = new HyperspaceAutomaton(params.w, params.h, 1.5f, 2.5f); 251 } 252 253 flickerTexture = Global.getSettings().getSprite(params.cat, params.key + "_glow"); 254 if (activeCells == null) { 255 activeCells = new CellStateTracker[params.w][params.h]; 256 257 if (savedActiveCells != null) { 258 for (CellStateTracker curr : savedActiveCells) { 259 activeCells[curr.i][curr.j] = curr; 260 } 261 } 262 } 263 264 // init cells to random mid-storm state where appropriate 265 int [][] cells = auto.getCells(); 266 267 for (int i = 0; i < activeCells.length; i++) { 268 for (int j = 0; j < activeCells[0].length; j++) { 269 if (tiles[i][j] < 0) continue; 270 271 CellStateTracker curr = activeCells[i][j]; 272 int val = cells[i][j]; 273 float interval = auto.getInterval().getIntervalDuration(); 274 275 if (val == 1 && curr == null) { 276 curr = activeCells[i][j] = new CellStateTracker(i, j, 277 interval * 0f + interval * 1.5f * (float) Math.random(), 278 interval * 0.5f + interval * 0.5f * (float) Math.random()); 279 280 float dur = (float) Math.random() * interval * 2.5f; 281 curr.advance(dur); 282 } 283 } 284 } 285 286 stormSoundId = getSpec().getCustom().optString("stormSound", null); 287 return this; 288 } 289 290 public CellStateTracker[][] getActiveCells() { 291 return activeCells; 292 } 293 294 protected static void clearCellsNotNearPlayer(HyperspaceTerrainPlugin plugin) { 295 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 296 if (playerFleet == null) return; 297 298 Vector2f test = new Vector2f(); 299 if (playerFleet != null) { 300 test = playerFleet.getLocationInHyperspace(); 301 } 302 303 float x = plugin.entity.getLocation().x; 304 float y = plugin.entity.getLocation().y; 305 float size = plugin.getTileSize(); 306 307 float w = plugin.tiles.length * size; 308 float h = plugin.tiles[0].length * size; 309 x -= w/2f; 310 y -= h/2f; 311 int xIndex = (int) ((test.x - x) / size); 312 int yIndex = (int) ((test.y - y) / size); 313 if (xIndex < 0) xIndex = 0; 314 if (yIndex < 0) yIndex = 0; 315 if (xIndex >= plugin.tiles.length) xIndex = plugin.tiles.length - 1; 316 if (yIndex >= plugin.tiles[0].length) yIndex = plugin.tiles[0].length - 1; 317 318 int subgridSize = (int) ((10000 / size + 1) * 2f); 319 320 int minX = Math.max(0, xIndex - subgridSize/2); 321 int maxX = xIndex + subgridSize/2 ; 322 int minY = Math.max(0, yIndex - subgridSize/2); 323 int maxY = yIndex + subgridSize/2; 324 325 // clean up area around the "active" area so that as the player moves around, 326 // they don't leave frozen storm cells behind (which would then make it into the savefile) 327 int pad = Math.max(plugin.tiles.length, plugin.tiles[0].length) * 2; 328 for (int i = minX - pad; i <= maxX + pad && i < plugin.tiles.length; i++) { 329 for (int j = minY - pad; j <= maxY + pad && j < plugin.tiles[0].length; j++) { 330 if (i < minX || j < minY || i > maxX || j > maxY) { 331 if (i >= 0 && j >= 0) { 332 plugin.activeCells[i][j] = null; 333 } 334 } 335 } 336 } 337 } 338 339 Object writeReplace() { 340 HyperspaceTerrainPlugin copy = (HyperspaceTerrainPlugin) super.writeReplace(); 341 342 clearCellsNotNearPlayer(copy); 343 344 copy.savedActiveCells = new ArrayList<CellStateTracker>(); 345 for (int i = 0; i < copy.activeCells.length; i++) { 346 for (int j = 0; j < copy.activeCells[0].length; j++) { 347 CellStateTracker curr = copy.activeCells[i][j]; 348 if (curr != null && isTileVisible(i, j)) { 349 copy.savedActiveCells.add(curr); 350 } 351 } 352 } 353 return copy; 354 } 355 356 transient private EnumSet<CampaignEngineLayers> layers = EnumSet.of(BASE, FLASH, GLOW, SHIVER, BASE_OVER, FLASH_OVER); 357 public EnumSet<CampaignEngineLayers> getActiveLayers() { 358 return layers; 359 } 360 361 362 protected transient float [] temp = new float[2]; 363 protected float[] getThetaAndRadius(Random rand, float width, float height) { 364 if (temp == null) temp = new float[2]; 365 366 //if (true) return temp; 367 float speedFactor = 0.5f; 368 369 float time = elapsed * Global.getSector().getClock().getSecondsPerDay(); 370 float min = -360f * (rand.nextFloat() * 3f + 1f) * Misc.RAD_PER_DEG; 371 float max = 360f * (rand.nextFloat() * 3f + 1f) * Misc.RAD_PER_DEG; 372 float rate = (30f + 70f * rand.nextFloat()) * Misc.RAD_PER_DEG; 373 rate *= speedFactor; 374 float period = 2f * (max - min) / rate; 375 float progress = rand.nextFloat() + time / period; 376 progress = progress - (int) progress; 377 378 float theta, radius; 379 if (progress < 0.5f) { 380 theta = min + (max - min) * progress * 2f; 381 } else { 382 theta = min + (max - min) * (1f - progress) * 2f; 383 } 384 temp[0] = theta; 385 386 min = 0f; 387 max = (width + height) * 0.025f; 388 rate = max * 0.5f; 389 rate *= speedFactor; 390 391 period = 2f * (max - min) / rate; 392 progress = rand.nextFloat() + time / period; 393 progress = progress - (int) progress; 394 if (progress < 0.5f) { 395 radius = min + (max - min) * progress * 2f; 396 } else { 397 radius = min + (max - min) * (1f - progress) * 2f; 398 } 399 temp[1] = radius; 400 401 return temp; 402 403// float twoPI = (float) Math.PI * 2f; 404// float maxRad = (width + height) * 0.025f; 405// float speedFactor = 0.5f; 406// float sign1 = rand.nextFloat() > 0.5f ? 1f : -1f; 407// float speed1 = (0.5f + rand.nextFloat()) * twoPI * speedFactor; 408// float theta1 = rand.nextFloat() * twoPI + speed1 * elapsed * sign1; 409// float radius1 = (0.5f + rand.nextFloat()) * maxRad; 410// temp[0] = theta1; 411// temp[1] = radius1; 412// return temp; 413 } 414 415 @Override 416 protected void renderQuad(int i, int j, float x, float y, float width, float height, 417 float texX, float texY, float texW, float texH, float angle) { 418 419 if (currLayer == null) { 420 super.renderQuad(i, j, x, y, width, height, texX, texY, texW, texH, angle); 421 return; 422 } 423 424 if (currLayer == FLASH_OVER) return; 425 if (currLayer == SHIVER) return; 426 //if (currLayer == BASE) return; 427 //if (currLayer == BASE_OVER) return; 428 //if (currLayer == FLASH) return; 429 //if (currLayer == GLOW) return; 430 431 432 CellStateTracker tracker = activeCells[i][j]; 433 float signal = 0f; 434 if (tracker != null) { 435 signal = tracker.getSignalBrightness(); 436 } 437 if (currLayer == FLASH && (tracker == null || tracker.flicker == null || tracker.flicker.getBrightness() <= 0)) { 438 return; 439 } 440 441 if (currLayer == GLOW && signal <= 0) { 442 return; 443 } 444 445 446// if (currLayer != HIGHLIGHT) return; 447// if (currLayer != UNDER) return; 448// if (currLayer == HIGHLIGHT) return; 449// if (currLayer != GLOW) return; 450// if (currLayer != OVER) return; 451 452 //if (currLayer != BASE) return; 453 //if (currLayer != BASE && currLayer != BASE_OVER) return; 454 455 long seed = (long) (x + y * tiles.length) * 1000000; 456// if (currLayer == BASE_OVER) { 457// seed /= (long) 4123; 458// } 459 460 Random rand = new Random(seed); 461 angle = rand.nextFloat() * 360f; 462 463 Color color = getRenderColor(); 464 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 465 if (playerFleet != null) { 466 float depth = getAbyssalDepth(playerFleet); 467 if (depth > 0) { 468 color = Misc.scaleColorOnly(color, Math.max(0f, 1f - depth)); 469 } 470 } 471 472 float [] tr = getThetaAndRadius(rand, width, height); 473 float theta1 = tr[0]; 474 float radius1 = tr[1]; 475 float sin1 = (float) Math.sin(theta1); 476 float cos1 = (float) Math.cos(theta1); 477 478 tr = getThetaAndRadius(rand, width, height); 479 float theta2 = tr[0]; 480 float radius2 = tr[1]; 481 float sin2 = (float) Math.sin(theta2); 482 float cos2 = (float) Math.cos(theta2); 483 484 tr = getThetaAndRadius(rand, width, height); 485 float theta3 = tr[0]; 486 float radius3 = tr[1]; 487 float sin3 = (float) Math.sin(theta3); 488 float cos3 = (float) Math.cos(theta3); 489 490 tr = getThetaAndRadius(rand, width, height); 491 float theta4 = tr[0]; 492 float radius4 = tr[1]; 493 float sin4 = (float) Math.sin(theta4); 494 float cos4 = (float) Math.cos(theta4); 495 496 497 498 float vw = width / 2f; 499 float vh = height / 2f; 500 501 502 float cx = x + vw; 503 float cy = y + vh; 504 505// vw *= 0.5f; 506// vh *= 0.5f; 507 508 float cos = (float) Math.cos(angle * Misc.RAD_PER_DEG); 509 float sin = (float) Math.sin(angle * Misc.RAD_PER_DEG); 510 511 float shiverThreshold = 0.75f; 512 513 boolean shiver = false; 514 boolean flicker = false; 515 516 //System.out.println("Layer: " + currLayer); 517 if (currLayer == FLASH || currLayer == FLASH_OVER) { 518 if (tracker != null && tracker.flicker != null && tracker.flicker.getBrightness() > 0) { 519 flicker = true; 520 } 521 } else if (currLayer == BASE) { 522 if (!currLayerColorSet) { 523 currLayerColorSet = true; 524 GL11.glColor4ub((byte)color.getRed(), 525 (byte)color.getGreen(), 526 (byte)color.getBlue(), 527 (byte)((float)color.getAlpha() * currAlpha * 1f)); 528 } 529 } else if (currLayer == BASE_OVER) { 530 if (!currLayerColorSet) { 531 currLayerColorSet = true; 532 GL11.glColor4ub((byte)color.getRed(), 533 (byte)color.getGreen(), 534 (byte)color.getBlue(), 535 (byte)((float)color.getAlpha() * currAlpha * 1f)); 536 } 537 } else if (currLayer == GLOW) { 538 if (tracker != null && signal > 0) { 539 GL11.glColor4ub((byte)color.getRed(), 540 (byte)color.getGreen(), 541 (byte)color.getBlue(), 542 (byte)((float)color.getAlpha() * currAlpha * 1f * signal)); 543 } else { 544 return; 545 } 546 } else if (currLayer == SHIVER) { 547 if (signal > shiverThreshold && tracker != null && tracker.flicker == null) { 548 shiver = true; 549 } 550 } else { 551 return; // under layer, but not "live" 552 } 553 554 if (currLayer == GLOW || currLayer == BASE || currLayer == BASE_OVER) { 555 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 556 //if (true) return; 557 int iter = 1; 558 if (currLayer == GLOW) iter = 1; 559 for (int k = 0; k < iter; k++) { 560 GL11.glTexCoord2f(texX, texY); 561 GL11.glVertex2f(cx + (-vw * cos + vh * sin) + sin1 * radius1, 562 cy + (-vw * sin - vh * cos) + cos1 * radius1); 563 564 GL11.glTexCoord2f(texX, texY + texH); 565 GL11.glVertex2f(cx + (-vw * cos - vh * sin) + sin2 * radius2, 566 cy + (-vw * sin + vh * cos) + cos2 * radius2); 567 568 GL11.glTexCoord2f(texX + texW, texY + texH); 569 GL11.glVertex2f(cx + (vw * cos - vh * sin) + sin3 * radius3, 570 cy + (vw * sin + vh * cos) + cos3 * radius3); 571 572 GL11.glTexCoord2f(texX + texW, texY); 573 GL11.glVertex2f(cx + (vw * cos + vh * sin) + sin4 * radius4, 574 cy + (vw * sin - vh * cos) + cos4 * radius4); 575 } 576 } 577 578 579 if (flicker || shiver) { 580 if (tracker == null) return; 581 if (shiver) return; 582 //float shiverBrightness = tracker.getBrightness(); 583 float shiverBrightness = (signal - shiverThreshold) / (1f - shiverThreshold); 584 if (shiverBrightness > 0.9f) { 585 shiverBrightness = (1f - shiverBrightness) / 0.1f; 586 } else { 587 shiverBrightness /= 0.9f; 588 } 589 //shiverBrightness *= shiverBrightness; 590 //shiverBrightness = 1f; 591 float ox = cx; 592 float oy = cy; 593 //float maxJitter = 0f + 30f * shiverBrightness * shiverBrightness; 594 float maxJitter = 0f + 30f; 595 //maxJitter = 0f; 596 if (shiver) { 597 rand.setSeed((long) (x + y * tiles.length) * 1000000 + Global.getSector().getClock().getTimestamp()); 598 maxJitter = 0f + 30f * shiverBrightness * shiverBrightness; 599 } else { 600 rand.setSeed((long) (x + y * tiles.length) * 1000000 + 601 (long) (tracker.flicker.getAngle() * 1000)); 602 } 603 maxJitter *= 5f; 604 if (shiver) { 605// vw *= 0.75f; 606// vh *= 0.75f; 607 } 608 if (flicker) { 609 //maxJitter = 0f; 610 //maxJitter *= 0.5f; 611 vw *= 1.5f; 612 vh *= 1.5f; 613 } 614 615 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 616 //flickerTexture.bindTexture(); 617 if (flicker) { 618 float alpha = currAlpha; 619 if (currLayer == FLASH_OVER) { 620 alpha *= 0.25f; 621 } 622 GL11.glColor4ub((byte)color.getRed(), 623 (byte)color.getGreen(), 624 (byte)color.getBlue(), 625 //(byte)((float)color.getAlpha() * currAlpha * shiverBrightness * 0.5f)); 626 (byte)((float)color.getAlpha() * alpha * tracker.flicker.getBrightness() * 1f)); 627 //System.out.println(tracker.flicker.getBrightness()); 628 } else if (shiver) { 629 GL11.glColor4ub((byte)color.getRed(), 630 (byte)color.getGreen(), 631 (byte)color.getBlue(), 632 (byte)((float)color.getAlpha() * currAlpha * shiverBrightness * 0.075f)); 633 } 634 635 int maxIter = 1; 636 if (shiver) maxIter = 5; 637 for (int iter = 0; iter < maxIter; iter++) { 638 cx = ox + rand.nextFloat() * maxJitter - maxJitter/2f; 639 cy = oy + rand.nextFloat() * maxJitter - maxJitter/2f; 640 641 GL11.glTexCoord2f(texX, texY); 642 GL11.glVertex2f(cx + (-vw * cos + vh * sin) + sin1 * radius1, 643 cy + (-vw * sin - vh * cos) + cos1 * radius1); 644 645 cx = ox + rand.nextFloat() * maxJitter - maxJitter/2f; 646 cy = oy + rand.nextFloat() * maxJitter - maxJitter/2f; 647 648 GL11.glTexCoord2f(texX, texY + texH); 649 GL11.glVertex2f(cx + (-vw * cos - vh * sin) + sin2 * radius2, 650 cy + (-vw * sin + vh * cos) + cos2 * radius2); 651 652 cx = ox + rand.nextFloat() * maxJitter - maxJitter/2f; 653 cy = oy + rand.nextFloat() * maxJitter - maxJitter/2f; 654 655 GL11.glTexCoord2f(texX + texW, texY + texH); 656 GL11.glVertex2f(cx + (vw * cos - vh * sin) + sin3 * radius3, 657 cy + (vw * sin + vh * cos) + cos3 * radius3); 658 659 cx = ox + rand.nextFloat() * maxJitter - maxJitter/2f; 660 cy = oy + rand.nextFloat() * maxJitter - maxJitter/2f; 661 662 GL11.glTexCoord2f(texX + texW, texY); 663 GL11.glVertex2f(cx + (vw * cos + vh * sin) + sin4 * radius4, 664 cy + (vw * sin - vh * cos) + cos4 * radius4); 665 } 666 } 667 } 668 669 670 671 public String getNebulaMapTex() { 672 return Global.getSettings().getSpriteName(params.cat, params.key + "_map"); 673 } 674 675 public String getNebulaTex() { 676 return Global.getSettings().getSpriteName(params.cat, params.key); 677 } 678 679 protected transient boolean clearedCellsPostLoad = false; 680 681 protected transient float stormCellTimeMultOutsideBaseArea = 0f; 682 public float getStormCellTimeMultOutsideBaseArea() { 683 return stormCellTimeMultOutsideBaseArea; 684 } 685 686 public void setStormCellTimeMultOutsideBaseArea(float stormCellTimeMultOutsideBaseArea) { 687 this.stormCellTimeMultOutsideBaseArea = stormCellTimeMultOutsideBaseArea; 688 } 689 protected transient float extraDistanceAroundPlayerToAdvanceStormCells = 0f; 690 public float getExtraDistanceAroundPlayerToAdvanceStormCells() { 691 return extraDistanceAroundPlayerToAdvanceStormCells; 692 } 693 694 public void setExtraDistanceAroundPlayerToAdvanceStormCells(float extraDistanceAroundPlayerToAdvanceStormCells) { 695 this.extraDistanceAroundPlayerToAdvanceStormCells = extraDistanceAroundPlayerToAdvanceStormCells; 696 } 697 698 699 public void advance(float amount) { 700 //if (true) return; 701 super.advance(amount); 702 703 getAbyssPlugin().advance(amount); 704 705 if (!clearedCellsPostLoad && Global.getSector().getPlayerFleet() != null) { 706 clearCellsNotNearPlayer(this); 707 clearedCellsPostLoad = true; 708 } 709 710 playStormStrikeSoundsIfNeeded(); 711 712 float days = Global.getSector().getClock().convertToDays(amount); 713// for (int i = 0; i < 100; i++) { 714// auto.advance(days * 10f); 715// } 716 auto.advance(days * 1f); 717 718 int [][] cells = auto.getCells(); 719 720// int count = 0; 721// for (int i = 0; i < activeCells.length; i++) { 722// for (int j = 0; j < activeCells[0].length; j++) { 723// if (tiles[i][j] < 0) continue; 724// CellStateTracker curr = activeCells[i][j]; 725// if (curr != null) { 726// count++; 727// } 728// } 729// } 730// System.out.println("Count: " + count + "(out of " + (activeCells.length * activeCells[0].length)); 731 732 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 733 Vector2f test = new Vector2f(); 734 if (playerFleet != null) { 735 test = playerFleet.getLocationInHyperspace(); 736 } 737 738 if (entity.getContainingLocation() != null && 739 playerFleet.getContainingLocation() == entity.getContainingLocation() && 740 isInAbyss(playerFleet)) { 741 742 float depth = getAbyssalDepth(playerFleet); 743 entity.getContainingLocation().getBackgroundColorShifter().shift( 744 "abyss_color", ABYSS_BACKGROUND_COLOR, 1f, 1f, depth); 745 746 entity.getContainingLocation().getBackgroundParticleColorShifter().shift( 747 "abyss_color", ABYSS_PARTICLE_COLOR, 1f, 1f, depth); 748 749 float gain = (float) getSpec().getCustom().optDouble("gain", 0.75f); 750 float gainHF = (float) getSpec().getCustom().optDouble("gainHF", 0.1f); 751 if (gain < 1f || gainHF < 1f) { 752 Global.getSoundPlayer().applyLowPassFilter( 753 Math.max(0f, 1f - (1f - gain) * depth), 754 Math.max(0f, 1f - (1f - gainHF) * depth)); 755 } 756 757// if (ABYSS_MUSIC_SUPPRESSION > 0f) { 758// Global.getSector().getCampaignUI().suppressMusic(ABYSS_MUSIC_SUPPRESSION * depth); 759// } 760 } 761 762 763 float x = this.entity.getLocation().x; 764 float y = this.entity.getLocation().y; 765 float size = getTileSize(); 766 767 float w = tiles.length * size; 768 float h = tiles[0].length * size; 769 x -= w/2f; 770 y -= h/2f; 771 int xIndex = (int) ((test.x - x) / size); 772 int yIndex = (int) ((test.y - y) / size); 773 if (xIndex < 0) xIndex = 0; 774 if (yIndex < 0) yIndex = 0; 775 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 776 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 777 778 float subgridDist = 10000f + extraDistanceAroundPlayerToAdvanceStormCells; 779 float baseSubgridDist = 10000f; 780 781 int subgridSize = (int) ((subgridDist / size + 1) * 2f); 782 783 int minX = Math.max(0, xIndex - subgridSize/2); 784 int maxX = xIndex + subgridSize/2 ; 785 int minY = Math.max(0, yIndex - subgridSize/2); 786 int maxY = yIndex + subgridSize/2; 787 788 int baseSubgridSize = (int) ((baseSubgridDist / size + 1) * 2f); 789 790 int baseMinX = Math.max(0, xIndex - baseSubgridSize/2); 791 int baseMaxX = xIndex + baseSubgridSize/2 ; 792 int baseMinY = Math.max(0, yIndex - baseSubgridSize/2); 793 int baseMaxY = yIndex + baseSubgridSize/2; 794 795 // clean up area around the "active" area so that as the player moves around, 796 // they don't leave frozen storm cells behind (which would then make it into the savefile) 797 int pad = 4; 798 for (int i = minX - pad; i <= maxX + pad && i < tiles.length; i++) { 799 for (int j = minY - pad; j <= maxY + pad && j < tiles[0].length; j++) { 800 if (i < minX || j < minY || i > maxX || j > maxY) { 801 if (i >= 0 && j >= 0) { 802 activeCells[i][j] = null; 803 } 804 } 805 } 806 } 807 808 for (int i = minX; i <= maxX && i < tiles.length; i++) { 809 for (int j = minY; j <= maxY && j < tiles[0].length; j++) { 810// for (int i = 0; i < activeCells.length; i++) { 811// for (int j = 0; j < activeCells[0].length; j++) { 812 if (tiles[i][j] < 0) continue; 813 814 CellStateTracker curr = activeCells[i][j]; 815 int val = cells[i][j]; 816 float interval = auto.getInterval().getIntervalDuration(); 817 818 if (val == 1 && curr == null) { 819 curr = activeCells[i][j] = new CellStateTracker(i, j, 820 interval * 0f + interval * 1.5f * (float) Math.random(), 821 interval * 0.5f + interval * 0.5f * (float) Math.random()); 822// interval * 0f + interval * 0.5f * (float) Math.random(), 823// interval * 0.25f + interval * 0.25f * (float) Math.random()); 824 } 825 826 if (curr != null) { 827 if (val != 1 && curr.isStorming() && !curr.isWaning()) { 828 //curr.wane(interval * 0.25f + interval * 0.25f * (float) Math.random()); 829 curr.wane(interval * 0.5f + interval * 0.5f * (float) Math.random()); 830// curr.wane(interval * 0.5f * (float) Math.random() + 831// interval * 0.25f + interval * 0.25f * (float) Math.random()); 832 } 833 float timeMult = 1f; 834 if (extraDistanceAroundPlayerToAdvanceStormCells > 0 && stormCellTimeMultOutsideBaseArea > 0) { 835 if (i < baseMinX || j < baseMinY || i > baseMaxX || j > baseMaxY) { 836 timeMult = stormCellTimeMultOutsideBaseArea; 837 } 838 } 839 curr.advance(days * timeMult); 840 if (curr.isOff()) { 841 activeCells[i][j] = null; 842 } 843 } 844 } 845 } 846 847 stormCellTimeMultOutsideBaseArea = 0f; 848 extraDistanceAroundPlayerToAdvanceStormCells = 0f; 849 } 850 851 852 protected void playStormStrikeSoundsIfNeeded() { 853 if (stormSoundId == null) return; 854 855 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 856 if (playerFleet.getContainingLocation() != entity.getContainingLocation()) return; 857 858 Vector2f test = playerFleet.getLocation(); 859 860 float x = this.entity.getLocation().x; 861 float y = this.entity.getLocation().y; 862 float size = getTileSize(); 863 864 float w = tiles.length * size; 865 float h = tiles[0].length * size; 866 867 x -= w/2f; 868 y -= h/2f; 869 870 int xIndex = (int) ((test.x - x) / size); 871 int yIndex = (int) ((test.y - y) / size); 872 873 if (xIndex < 0) xIndex = 0; 874 if (yIndex < 0) yIndex = 0; 875 876 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 877 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 878 879 int subgridSize = (int) ((STORM_STRIKE_SOUND_RANGE / size + 1) * 2f); 880 881 for (float i = Math.max(0, xIndex - subgridSize/2); i <= xIndex + subgridSize/2 && i < tiles.length; i++) { 882 for (float j = Math.max(0, yIndex - subgridSize/2); j <= yIndex + subgridSize/2 && j < tiles[0].length; j++) { 883 int texIndex = tiles[(int) i][(int) j]; 884 if (texIndex >= 0) { 885 float tcx = x + i * size + size/2f; 886 float tcy = y + j * size + size/2f; 887 Vector2f tileLoc = new Vector2f(tcx, tcy); 888 889 CellStateTracker curr = activeCells[(int)i][(int)j]; 890 if (curr == null || curr.flicker == null || !curr.isStorming() || !curr.flicker.isPeakFrame() || curr.flicker.getNumBursts() > 1) continue; 891 892 float dist = Misc.getDistance(test, tileLoc); 893 if (dist > STORM_STRIKE_SOUND_RANGE) continue; 894 895 // will be attenuated without this, but there's "too much" lightning sound without 896 // this additional attenuation 897 float volumeMult = 1f - (dist / STORM_STRIKE_SOUND_RANGE); 898 volumeMult = (float) Math.sqrt(volumeMult); 899 if (volumeMult <= 0) continue; 900 //float volumeMult = 1f; 901 //volumeMult *= 0.67f; 902 Global.getSoundPlayer().playSound(stormSoundId, 1f, 1f * volumeMult, tileLoc, Misc.ZERO); 903 } 904 } 905 } 906 907 } 908 909// protected void spawnWavefront(int i, int j) { 910// if (true) return; 911// float [] center = getTileCenter(i, j); 912// 913// float angle; 914// float spread = 90f; 915// int yLoc = (int) center[1]; 916// yLoc = yLoc / 4000; 917// if (yLoc % 2 == 0) { 918// angle = 0 - spread / 2f + (float) Math.random() * spread; 919// } else { 920// angle = 180 - spread / 2f + (float) Math.random() * spread; 921// } 922// 923// float initialRange = 400f; 924// Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle); 925// loc.scale(50f); 926// loc.x += center[0]; 927// loc.y += center[1]; 928// 929// float burnLevel = (float) (7f + 8f * Math.random()); 930// burnLevel = Math.round(burnLevel); 931// 932// float r = (float) Math.random(); 933// r *= r; 934// float durDays = 1f + r * 2f; 935// float width = 300f + 500f * (float) Math.random(); 936// SectorEntityToken wave = entity.getContainingLocation().addTerrain( 937// Terrain.WAVEFRONT, 938// new WavefrontParams(burnLevel, // burn level 939// 1f, // CR loss multiplier 940// initialRange, // "origin" range, controls curve of wave 941// width, 100, // width and width expansion 942// 200f, 10, // thickness and thickness expansion 943// durDays, // duration days 944// angle // angle 945// ) { 946// 947// }); 948// wave.getLocation().set(loc.x, loc.y); 949// } 950 951 952 953 954 private transient CampaignEngineLayers currLayer = null; 955 private transient boolean currLayerColorSet = false; 956 private transient float currAlpha = 1f; 957 public void render(CampaignEngineLayers layer, ViewportAPI viewport) { 958 //if (true) return; 959 currLayer = layer; 960 //currLayerColorSet = false; 961 super.render(layer, viewport); 962 } 963 964 @Override 965 public void renderOnMap(float factor, float alphaMult) { 966 currLayer = null; 967 //currLayerColorSet = false; 968 super.renderOnMap(factor, alphaMult); 969 } 970 971 972 973 @Override 974 public float getTileRenderSize() { 975 //return TILE_SIZE + 300f; 976 //return TILE_SIZE + 600f; 977 return TILE_SIZE * 2.5f; 978 } 979 980 @Override 981 public float getTileContainsSize() { 982 //return TILE_SIZE + 200f; 983 return TILE_SIZE * 1.5f; 984 } 985 986 @Override 987 public float getTileSize() { 988 return TILE_SIZE; 989 } 990 991 @Override 992 protected void renderSubArea(float startColumn, float endColumn, 993 float startRow, float endRow, float factor, int samples, 994 float alphaMult) { 995 super.renderSubArea(startColumn, endColumn, startRow, endRow, factor, samples, alphaMult); 996 } 997 998 @Override 999 public void preRender(CampaignEngineLayers layer, float alphaMult) { 1000 GL11.glEnable(GL11.GL_BLEND); 1001 1002 //System.out.println("Layer: " + layer); 1003 1004 if (layer == FLASH || layer == FLASH_OVER) { 1005 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 1006 flickerTexture.bindTexture(); 1007 } else { 1008 if (layer == GLOW || layer == SHIVER || layer == BASE) { 1009 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 1010 } else { 1011 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 1012 } 1013 if (layer == SHIVER) { 1014 flickerTexture.bindTexture(); 1015 } 1016 } 1017 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 1018 1019// if (layer == UPPER) { 1020// alphaMult *= 0.30f; 1021// } 1022 1023 currAlpha = alphaMult; 1024 currLayerColorSet = false; 1025// Color color = getRenderColor(); 1026// GL11.glColor4ub((byte)color.getRed(), 1027// (byte)color.getGreen(), 1028// (byte)color.getBlue(), 1029// (byte)((float)color.getAlpha() * alphaMult)); 1030 } 1031 1032 @Override 1033 public void preMapRender(float alphaMult) { 1034 GL11.glEnable(GL11.GL_BLEND); 1035 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 1036 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 1037 1038 //Color color = new Color(125,125,200,255); 1039 //Color color = new Color(100,100,150,255); 1040 currAlpha = alphaMult; 1041 currLayerColorSet = false; 1042 1043 Color color = getRenderColor(); 1044 GL11.glColor4ub((byte)color.getRed(), 1045 (byte)color.getGreen(), 1046 (byte)color.getBlue(), 1047 (byte)((float)color.getAlpha() * alphaMult)); 1048 } 1049 1050 1051 public void renderOnRadar(Vector2f radarCenter, float factor, float alphaMult) { 1052 currLayer = null; 1053 //if (true) return; 1054 1055 float radius = Global.getSettings().getFloat("campaignRadarRadius") + 2000; 1056 1057 GL11.glPushMatrix(); 1058 GL11.glTranslatef(-radarCenter.x * factor, -radarCenter.y * factor, 0); 1059 //super.renderOnMap(factor, alphaMult); 1060 1061 preMapRender(alphaMult); 1062 //GL11.glDisable(GL11.GL_TEXTURE_2D); 1063 1064 int samples = 10; 1065 1066 float x = this.entity.getLocation().x; 1067 float y = this.entity.getLocation().y; 1068 float size = getTileSize(); 1069 float renderSize = getTileRenderSize(); 1070 1071 float w = tiles.length * size; 1072 float h = tiles[0].length * size; 1073 x -= w/2f; 1074 y -= h/2f; 1075 float extra = (renderSize - size) / 2f + 100f; 1076 1077 float llx = radarCenter.x - radius; 1078 float lly = radarCenter.y - radius; 1079 float vw = radius * 2f; 1080 float vh = radius * 2f; 1081 1082 if (llx > x + w + extra) { 1083 GL11.glPopMatrix(); 1084 return; 1085 } 1086 if (lly > y + h + extra) { 1087 GL11.glPopMatrix(); 1088 return; 1089 } 1090 if (llx + vw + extra < x) { 1091 GL11.glPopMatrix(); 1092 return; 1093 } 1094 if (lly + vh + extra < y) { 1095 GL11.glPopMatrix(); 1096 return; 1097 } 1098 1099 float xStart = (int)((llx - x - extra) / size); 1100 if (xStart < 0) xStart = 0; 1101 float yStart = (int)((lly - y - extra) / size); 1102 if (yStart < 0) yStart = 0; 1103 1104 float xEnd = (int)((llx + vw - x + extra) / size) + 1; 1105 if (xEnd >= tiles.length) xEnd = tiles.length - 1; 1106 float yEnd = (int)((lly + vw - y + extra) / size) + 1; 1107 if (yEnd >= tiles.length) yEnd = tiles[0].length - 1; 1108 1109 xStart = (int) Math.floor(xStart / samples) * samples; 1110 xEnd = (int) Math.floor(xEnd / samples) * samples; 1111 yStart = (int) Math.ceil(yStart / samples) * samples; 1112 yEnd = (int) Math.ceil(yEnd / samples) * samples; 1113 1114 mapTexture.bindTexture(); 1115 GL11.glEnable(GL11.GL_TEXTURE_2D); 1116 renderSubArea(xStart, xEnd, yStart, yEnd, factor, samples, alphaMult); 1117 1118 GL11.glPopMatrix(); 1119 } 1120 1121 1122 @Override 1123 public Color getRenderColor() { 1124 return Color.white; 1125 //return Misc.scaleColorOnly(Color.white, 0.67f); 1126 } 1127 1128 @Override 1129 public boolean containsEntity(SectorEntityToken other) { 1130 //if (!isPreventedFromAffecting(other)) return false; 1131// if (isPreventedFromAffecting(other)) { 1132// return !isInClouds(other); 1133// } 1134 return true; 1135 } 1136 1137 @Override 1138 public float getRenderRange() { 1139 return Float.MAX_VALUE; 1140 } 1141 1142 @Override 1143 public boolean containsPoint(Vector2f test, float r) { 1144 return true; 1145 } 1146 1147 public float getAbyssalDepth(Vector2f loc) { 1148 return getAbyssPlugin().getAbyssalDepth(loc); 1149 } 1150 public float getAbyssalDepth(Vector2f loc, boolean uncapped) { 1151 return getAbyssPlugin().getAbyssalDepth(loc, uncapped); 1152 } 1153 public float getAbyssalDepth(SectorEntityToken other) { 1154 return getAbyssPlugin().getAbyssalDepth(other); 1155 } 1156 public float getAbyssalDepth(SectorEntityToken other, boolean uncapped) { 1157 return getAbyssPlugin().getAbyssalDepth(other, uncapped); 1158 } 1159 public boolean isInAbyss(SectorEntityToken other) { 1160 return getAbyssPlugin().isInAbyss(other); 1161 } 1162 1163 public List<StarSystemAPI> getAbyssalSystems() { 1164 return getAbyssPlugin().getAbyssalSystems(); 1165 } 1166 1167 public boolean isInClouds(SectorEntityToken other) { 1168 if (other == null) return false; 1169 if (other.getContainingLocation() != this.entity.getContainingLocation()) return false; 1170 if (isPreventedFromAffecting(other)) return false; 1171 return super.containsPoint(other.getLocation(), other.getRadius()); 1172 } 1173 1174 public boolean isInClouds(Vector2f test, float r) { 1175 return super.containsPoint(test, r); 1176 } 1177 1178 public int [] getTilePreferStorm(Vector2f test, float r) { 1179 // tiles exist outside render range now 1180 //float dist = Misc.getDistance(this.entity.getLocation(), test) - r; 1181 //if (dist > getRenderRange()) return null; 1182 1183 float x = this.entity.getLocation().x; 1184 float y = this.entity.getLocation().y; 1185 float size = getTileSize(); 1186 float containsSize = getTileContainsSize(); 1187 1188 float w = tiles.length * size; 1189 float h = tiles[0].length * size; 1190 1191 x -= w/2f; 1192 y -= h/2f; 1193 1194 float extra = (containsSize - size) / 2f; 1195 1196 if (test.x + r + extra < x) return null; 1197 if (test.y + r + extra < y) return null; 1198 if (test.x > x + w + r + extra) return null; 1199 if (test.y > y + h + r + extra) return null; 1200 1201 int xIndex = (int) ((test.x - x) / size); 1202 int yIndex = (int) ((test.y - y) / size); 1203 1204 if (xIndex < 0) xIndex = 0; 1205 if (yIndex < 0) yIndex = 0; 1206 1207 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 1208 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 1209 1210 int [] found = null; 1211 for (float i = Math.max(0, xIndex - 1); i <= xIndex + 1 && i < tiles.length; i++) { 1212 for (float j = Math.max(0, yIndex - 1); j <= yIndex + 1 && j < tiles[0].length; j++) { 1213 int texIndex = tiles[(int) i][(int) j]; 1214 if (texIndex >= 0) { 1215 float tx = x + i * size + size/2f - containsSize/2f; 1216 float ty = y + j * size + size/2f - containsSize/2f; 1217 1218 if (test.x + r < tx) continue; 1219 if (test.y + r < ty) continue; 1220 if (test.x > tx + containsSize + r) continue; 1221 if (test.y > ty + containsSize + r) continue; 1222 //return true; 1223 int [] curr = new int[] {(int)i, (int)j}; 1224 //int val = auto.getCells()[(int) i][(int) j]; 1225 //if (val == 1) { 1226 CellStateTracker cell = activeCells[(int) i][(int) j]; 1227 if (cell != null && cell.isStorming()) { 1228 return curr; 1229 } 1230 if (found == null || (cell != null && cell.isSignaling())) { 1231 found = curr; 1232 } 1233 } 1234 } 1235 } 1236 return found; 1237 } 1238 public CellStateTracker getExactCellAt(Vector2f location) { 1239 int [] tile = getTile(location); 1240 CellStateTracker cell = null; 1241 if (tile != null) { 1242 cell = activeCells[tile[0]][tile[1]]; 1243 } 1244 return cell; 1245 } 1246 1247 public int [] getTile(Vector2f test) { 1248 float x = this.entity.getLocation().x; 1249 float y = this.entity.getLocation().y; 1250 float size = getTileSize(); 1251 float containsSize = getTileContainsSize(); 1252 1253 float w = tiles.length * size; 1254 float h = tiles[0].length * size; 1255 1256 x -= w/2f; 1257 y -= h/2f; 1258 1259 float extra = (containsSize - size) / 2f; 1260 1261 if (test.x + extra < x) return null; 1262 if (test.y + extra < y) return null; 1263 if (test.x > x + w + extra) return null; 1264 if (test.y > y + h + extra) return null; 1265 1266 int xIndex = (int) ((test.x - x) / size); 1267 int yIndex = (int) ((test.y - y) / size); 1268 1269 if (xIndex < 0) xIndex = 0; 1270 if (yIndex < 0) yIndex = 0; 1271 1272 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 1273 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 1274 1275 return new int[] {xIndex, yIndex}; 1276 } 1277 1278 public LocationState getStateAt(SectorEntityToken entity, float extraRadius) { 1279 boolean inCloud = isInClouds(entity); 1280 int [] tile = getTilePreferStorm(entity.getLocation(), entity.getRadius() + extraRadius); 1281 CellStateTracker cell = null; 1282 if (tile != null) { 1283 cell = activeCells[tile[0]][tile[1]]; 1284 } 1285 if (!inCloud) { 1286 return LocationState.OPEN; 1287 } else if (cell == null || !cell.isStorming()) { 1288 return LocationState.DEEP; 1289 } else { //if (cell.isStorming()) { 1290 return LocationState.DEEP_STORM; 1291 } 1292 } 1293 1294 public CellStateTracker getCellAt(Vector2f location, float radius) { 1295 int [] tile = getTilePreferStorm(location, radius); 1296 CellStateTracker cell = null; 1297 if (tile != null) { 1298 cell = activeCells[tile[0]][tile[1]]; 1299 } 1300 return cell; 1301 } 1302 1303 public CellStateTracker getCellAt(SectorEntityToken entity, float extraRadius) { 1304 int [] tile = getTilePreferStorm(entity.getLocation(), entity.getRadius() + extraRadius); 1305 CellStateTracker cell = null; 1306 if (tile != null) { 1307 cell = activeCells[tile[0]][tile[1]]; 1308 } 1309 return cell; 1310 } 1311 1312 @Override 1313 protected boolean shouldPlayLoopOne() { 1314 LocationState state = getStateAt(Global.getSector().getPlayerFleet(), getExtraSoundRadius()); 1315 return super.shouldPlayLoopOne() && state == LocationState.OPEN; 1316 } 1317 1318 @Override 1319 protected boolean shouldPlayLoopTwo() { 1320 LocationState state = getStateAt(Global.getSector().getPlayerFleet(), getExtraSoundRadius()); 1321 //System.out.println("Two: " + (super.shouldPlayLoopTwo() && state == LocationState.DEEP)); 1322 return super.shouldPlayLoopTwo() && state == LocationState.DEEP; 1323 } 1324 1325 @Override 1326 protected boolean shouldPlayLoopThree() { 1327 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 1328 return super.shouldPlayLoopThree() && isInAbyss(playerFleet); 1329 } 1330 1331 @Override 1332 public float getProximitySoundFactor() { 1333 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 1334 float depth = getAbyssalDepth(playerFleet); 1335 if (depth > 0f) { 1336 return depth; 1337 } 1338 1339 return super.getProximitySoundFactor(); 1340 } 1341 1342 @Override 1343 protected boolean shouldPlayLoopFour() { 1344 LocationState state = getStateAt(Global.getSector().getPlayerFleet(), getExtraSoundRadius()); 1345 //System.out.println("Four: " + (super.shouldPlayLoopFour() && state == LocationState.DEEP_STORM)); 1346 return super.shouldPlayLoopFour() && state == LocationState.DEEP_STORM; 1347 } 1348 1349 1350 @Override 1351 public void applyEffect(SectorEntityToken entity, float days) { 1352 float depth = getAbyssalDepth(entity); 1353 if (depth > 0) { 1354 if (abyssDarkSource == null) { 1355 abyssDarkSource = entity.getContainingLocation().createToken(0, 0); 1356 abyssDarkSource.addTag(Tags.AMBIENT_LS); 1357 } 1358 1359 Color from = this.entity.getLightColor(); 1360 if (from == null) from = Color.white; 1361 Color c = Misc.interpolateColor(from, ABYSS_LIGHT_COLOR, depth); 1362 entity.getMemoryWithoutUpdate().set(MemFlags.LIGHT_SOURCE_OVERRIDE, abyssDarkSource, 0.1f); 1363 entity.getMemoryWithoutUpdate().set(MemFlags.LIGHT_SOURCE_COLOR_OVERRIDE, c, 0.1f); 1364 1365 if (entity instanceof CampaignFleetAPI) { 1366 CampaignFleetAPI fleet = (CampaignFleetAPI) entity; 1367 for (FleetMemberViewAPI view : fleet.getViews()) { 1368 //view.getContrailColor().shift(getModId(), Misc.zeroColor, 1f, 1f, depth * 0.25f); 1369 view.getContrailWidthMult().shift(getModId(), 0.5f, 1f, 1f, depth); 1370 view.getContrailDurMult().shift(getModId(), 1f + depth * 1f, 1f, 1f, depth); 1371 view.getEngineGlowSizeMult().shift(getModId(), 2f, 1f, 1f, 1f - depth * 0.5f); 1372 view.getEngineGlowColor().shift(getModId(), Color.black, 1f, 1f, depth * 0.5f); 1373 } 1374 } 1375 } 1376 1377 if (entity instanceof CampaignFleetAPI) { 1378 CampaignFleetAPI fleet = (CampaignFleetAPI) entity; 1379 1380 boolean inAbyss = depth > 0; 1381 boolean inCloud = isInClouds(fleet); 1382 int [] tile = getTilePreferStorm(fleet.getLocation(), fleet.getRadius()); 1383 CellStateTracker cell = null; 1384 if (tile != null) { 1385 cell = activeCells[tile[0]][tile[1]]; 1386 } 1387 1388// if (cell == null) { 1389// inCloud = false; 1390// } 1391 1392// fleet.getStats().addTemporaryModFlat(0.1f, getModId() + "_fuel", 1393// "In hyperspace", FUEL_USE_FRACTION, 1394// fleet.getStats().getFuelUseHyperMult()); 1395 1396 if (inAbyss) { 1397 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_3", 1398 "In abyssal hyperspace", 1f - (1f - ABYSS_VISIBLITY_MULT) * depth, 1399 fleet.getStats().getDetectedRangeMod()); 1400 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_4", 1401 "In abyssal hyperspace", 1f - (1f - ABYSS_SENSOR_RANGE_MULT) * depth, 1402 fleet.getStats().getSensorRangeMod()); 1403 1404 //ABYSS_NAVIGATION_EFFECT = 0.25f; 1405 float skillMod = fleet.getCommanderStats().getDynamic().getValue(Stats.NAVIGATION_PENALTY_MULT); 1406 //skillMod = 1f; 1407 skillMod = skillMod + (1f - skillMod) * (1f - ABYSS_NAVIGATION_EFFECT); 1408 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_5", 1409 "In abyssal hyperspace", 1f - (1f - ABYSS_BURN_MULT) * depth * skillMod, 1410 fleet.getStats().getFleetwideMaxBurnMod()); 1411 } 1412 1413 1414 if ((!inCloud && !inAbyss) || fleet.isInHyperspaceTransition()) { 1415 // open, do nothing 1416 //} else if (cell == null || !cell.isStorming()) { 1417 } else if (inCloud) { 1418 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_1", 1419 "In deep hyperspace", VISIBLITY_MULT, 1420 fleet.getStats().getDetectedRangeMod()); 1421 1422 //float penalty = getBurnPenalty(fleet); 1423 float penalty = Misc.getBurnMultForTerrain(fleet); 1424 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_2", 1425 "In deep hyperspace", penalty, 1426 fleet.getStats().getFleetwideMaxBurnMod()); 1427 if (cell != null && cell.isSignaling() && cell.signal < 0.2f) { 1428 cell.signal = 0; // go to storm as soon as a fleet enters, if it's close to storming already 1429 } 1430 if (cell != null && cell.isStorming() && !Misc.isSlowMoving(fleet) && fleet.getBattle() == null) { 1431 // storm 1432 if (STORM_SENSOR_RANGE_MULT != 1) { 1433 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_storm_sensor", 1434 "In deep hyperspace (storm)", STORM_SENSOR_RANGE_MULT, 1435 fleet.getStats().getSensorRangeMod()); 1436 } 1437 1438 if (STORM_VISIBILITY_FLAT != 0) { 1439 fleet.getStats().addTemporaryModFlat(0.1f, getModId() + "_storm_visibility", 1440 "In deep hyperspace (storm)", STORM_VISIBILITY_FLAT, 1441 fleet.getStats().getDetectedRangeMod()); 1442 } 1443 1444 if (STORM_SPEED_MULT != 1) { 1445 fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_storm_speed", 1446 "In deep hyperspace (storm)", getAdjustedSpeedMult(fleet, STORM_SPEED_MULT), 1447 fleet.getStats().getFleetwideMaxBurnMod()); 1448 } 1449 applyStormStrikes(cell, fleet, days); 1450 } 1451 } 1452 } 1453 } 1454 1455 protected void applyStormStrikes(CellStateTracker cell, CampaignFleetAPI fleet, float days) { 1456 1457 if (cell.flicker != null && cell.flicker.getWait() > 0) { 1458 cell.flicker.setNumBursts(0); 1459 cell.flicker.setWait(0); 1460 cell.flicker.newBurst(); 1461 } 1462 1463 if (cell.flicker == null || !cell.flicker.isPeakFrame()) return; 1464 1465 1466 fleet.addScript(new HyperStormBoost(cell, fleet)); 1467 1468 String key = STORM_STRIKE_TIMEOUT_KEY; 1469 MemoryAPI mem = fleet.getMemoryWithoutUpdate(); 1470 if (mem.contains(key)) return; 1471 //boolean canDamage = !mem.contains(key); 1472 mem.set(key, true, (float) (STORM_MIN_TIMEOUT + (STORM_MAX_TIMEOUT - STORM_MIN_TIMEOUT) * Math.random())); 1473 1474 //if ((float) Math.random() > STORM_STRIKE_CHANCE && false) return; 1475 1476 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy(); 1477 if (members.isEmpty()) return; 1478 1479 float totalValue = 0; 1480 for (FleetMemberAPI member : members) { 1481 totalValue += member.getStats().getSuppliesToRecover().getModifiedValue(); 1482 } 1483 if (totalValue <= 0) return; 1484 1485 float strikeValue = totalValue * STORM_DAMAGE_FRACTION * (0.5f + (float) Math.random() * 0.5f); 1486 1487// int index = Misc.random.nextInt(members.size()); 1488// FleetMemberAPI member = members.get(index); 1489 1490 float ebCostThresholdMult = 4f; 1491 1492 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(); 1493 WeightedRandomPicker<FleetMemberAPI> preferNotTo = new WeightedRandomPicker<FleetMemberAPI>(); 1494 for (FleetMemberAPI member : members) { 1495 float w = 1f; 1496 if (member.isMothballed()) w *= 0.1f; 1497 1498 1499 float ebCost = EmergencyBurnAbility.getCRCost(member, fleet); 1500 if (ebCost * ebCostThresholdMult > member.getRepairTracker().getCR()) { 1501 preferNotTo.add(member, w); 1502 } else { 1503 picker.add(member, w); 1504 } 1505 } 1506 if (picker.isEmpty()) { 1507 picker.addAll(preferNotTo); 1508 } 1509 1510 FleetMemberAPI member = picker.pick(); 1511 if (member == null) return; 1512 1513 float crPerDep = member.getDeployCost(); 1514 float suppliesPerDep = member.getStats().getSuppliesToRecover().getModifiedValue(); 1515 if (suppliesPerDep <= 0 || crPerDep <= 0) return; 1516 1517 float strikeDamage = crPerDep * strikeValue / suppliesPerDep; 1518 if (strikeDamage < STORM_MIN_STRIKE_DAMAGE) strikeDamage = STORM_MIN_STRIKE_DAMAGE; 1519 1520 float resistance = member.getStats().getDynamic().getValue(Stats.CORONA_EFFECT_MULT); 1521 strikeDamage *= resistance; 1522 1523 if (strikeDamage > STORM_MAX_STRIKE_DAMAGE) strikeDamage = STORM_MAX_STRIKE_DAMAGE; 1524 1525// if (fleet.isPlayerFleet()) { 1526// System.out.println("wefw34gerg"); 1527// } 1528 1529 float currCR = member.getRepairTracker().getBaseCR(); 1530 float crDamage = Math.min(currCR, strikeDamage); 1531 1532 float ebCost = EmergencyBurnAbility.getCRCost(member, fleet); 1533 if (currCR >= ebCost * ebCostThresholdMult) { 1534 crDamage = Math.min(currCR - ebCost * 1.5f, crDamage); 1535 } 1536 1537 if (crDamage > 0) { 1538 member.getRepairTracker().applyCREvent(-crDamage, "hyperstorm", "Hyperspace storm strike"); 1539 } 1540 1541 float hitStrength = member.getStats().getArmorBonus().computeEffective(member.getHullSpec().getArmorRating()); 1542 hitStrength *= strikeDamage / crPerDep; 1543 if (hitStrength > 0) { 1544 member.getStatus().applyDamage(hitStrength); 1545 if (member.getStatus().getHullFraction() < 0.01f) { 1546 member.getStatus().setHullFraction(0.01f); 1547 } 1548 } 1549 1550 if (fleet.isPlayerFleet()) { 1551 String verb = "suffers"; 1552 Color c = Misc.getNegativeHighlightColor(); 1553 if (hitStrength <= 0) { 1554 verb = "avoids"; 1555 //c = Misc.getPositiveHighlightColor(); 1556 c = Misc.getTextColor(); 1557 } 1558 Global.getSector().getCampaignUI().addMessage( 1559 member.getShipName() + " " + verb + " damage from the storm", c); 1560 1561 Global.getSector().getCampaignUI().showHelpPopupIfPossible("chmHyperStorm"); 1562 } 1563 } 1564 1565 public String getStormSoundId() { 1566 return stormSoundId; 1567 } 1568 1569 public boolean hasTooltip() { 1570 return true; 1571 } 1572 1573 public String getNameForTooltip() { 1574 return getTerrainName(); 1575 } 1576 1577 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) { 1578 float pad = 10f; 1579 float small = 5f; 1580 Color gray = Misc.getGrayColor(); 1581 Color highlight = Misc.getHighlightColor(); 1582 Color fuel = Global.getSettings().getColor("progressBarFuelColor"); 1583 Color bad = Misc.getNegativeHighlightColor(); 1584 1585 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 1586 boolean inCloud = isInClouds(player); 1587 float depth = getAbyssalDepth(player); 1588 boolean inAbyss = depth > 0f; 1589 int [] tile = getTilePreferStorm(player.getLocation(), player.getRadius()); 1590 CellStateTracker cell = null; 1591 if (tile != null) { 1592 cell = activeCells[tile[0]][tile[1]]; 1593 } 1594// if (cell == null) { 1595// inCloud = false; 1596// } 1597 1598 tooltip.addTitle(getTerrainName()); 1599 if (inAbyss) { 1600 //abyssal 1601 tooltip.addPara(Global.getSettings().getDescription(getTerrainId() + "_abyssal", Type.TERRAIN).getText1(), pad); 1602 } else if (!inCloud) { 1603 // open 1604 tooltip.addPara(Global.getSettings().getDescription(getTerrainId() + "_normal", Type.TERRAIN).getText1(), pad); 1605 } else if (cell == null || !cell.isStorming()) { 1606 // deep 1607 tooltip.addPara(Global.getSettings().getDescription(getTerrainId() + "_deep", Type.TERRAIN).getText1(), pad); 1608 } else if (cell.isStorming()) { 1609 // storm 1610 tooltip.addPara(Global.getSettings().getDescription(getTerrainId() + "_storm", Type.TERRAIN).getText1(), pad); 1611 } 1612 1613 String fuelCost = Misc.getRoundedValueMaxOneAfterDecimal(player.getLogistics().getFuelCostPerLightYear()); 1614 1615 float nextPad = pad; 1616 if (expanded) { 1617 tooltip.addSectionHeading("Travel", Alignment.MID, pad); 1618 nextPad = small; 1619 } 1620 tooltip.addPara("Traveling through hyperspace consumes fuel based on the distance travelled. " + 1621 "Your fleet requires %s fuel per light-year.*", nextPad, 1622 highlight, fuelCost); 1623 1624 if (inAbyss) { 1625 tooltip.addPara("Reduces the sensor range and sensor profile of fleets inside it by %s. " 1626 + "Also reduces the maximum burn level by %s. The reduction is gradual and based " 1627 + "on the \"depth\" the fleet has reached.", 1628 pad, 1629 highlight, 1630 "" + (int) Math.round((1f - ABYSS_VISIBLITY_MULT) * 100f) + "%", 1631 "" + (int) Math.round((1f - ABYSS_BURN_MULT) * 100f) + "%" 1632 ); 1633 if (ABYSS_NAVIGATION_EFFECT <= 0) { 1634 tooltip.addPara("Skill in navigation is of little use, and does not provide its " 1635 + "normal benefit in countering terrain-specific maximum burn penalties.", pad, 1636 highlight, 1637 "" + Math.round(ABYSS_NAVIGATION_EFFECT * 100f) + "%"); 1638 } else { 1639 tooltip.addPara("Skill in navigation is of limited use, and only provides %s of its " 1640 + "normal benefit in countering the maximum burn penalty.", pad, 1641 highlight, 1642 "" + Math.round(ABYSS_NAVIGATION_EFFECT * 100f) + "%"); 1643 } 1644 } else if (inCloud) { 1645 tooltip.addPara("Reduces the range at which fleets inside can be detected by %s.", 1646 pad, 1647 highlight, 1648 "" + (int) Math.round((1f - VISIBLITY_MULT) * 100f) + "%" 1649 ); 1650 1651 tooltip.addPara("Reduces the speed of fleets inside by up to %s. Larger fleets are slowed down more.", 1652 nextPad, 1653 highlight, 1654 "" + (int) Math.round((Misc.BURN_PENALTY_MULT) * 100f) + "%" 1655 ); 1656 1657 float penalty = Misc.getBurnMultForTerrain(Global.getSector().getPlayerFleet()); 1658 tooltip.addPara("Your fleet's speed is reduced by %s.", pad, 1659 highlight, 1660 "" + (int) Math.round((1f - penalty) * 100f) + "%" 1661 //Strings.X + penaltyStr 1662 ); 1663 1664 tooltip.addSectionHeading("Hyperspace storms", Alignment.MID, pad); 1665 1666 Color stormDescColor = Misc.getTextColor(); 1667 if (cell != null && cell.isStorming()) { 1668 stormDescColor = bad; 1669 } 1670 tooltip.addPara("Being caught in a storm causes storm strikes to damage ships " + 1671 "and reduce their combat readiness. " + 1672 "Larger fleets attract more damaging strikes.", stormDescColor, pad); 1673 1674 tooltip.addPara("In addition, storm strikes toss the fleet's drive bubble about " + 1675 "with great violence, often causing a loss of control. " + 1676 "Some commanders are known to use these to gain additional " + 1677 "speed, and to save fuel - a practice known as \"storm riding\".", Misc.getTextColor(), pad); 1678 1679 tooltip.addPara("\"Slow-moving\" fleets do not attract storm strikes.", Misc.getTextColor(), pad); 1680 } 1681 1682 if (expanded) { 1683 tooltip.addSectionHeading("Combat", Alignment.MID, pad); 1684// if (inCloud) { 1685// tooltip.addPara("Numerous patches of nebula-like hyperfragments present on the battlefield, slowing ships down to a percentage of their top speed.", small); 1686// } else { 1687// tooltip.addPara("No effect.", small); 1688// } 1689 if (inAbyss) { 1690// public static float ABYSS_SHIP_SPEED_PENALTY = 20f; 1691// public static float ABYSS_MISSILE_SPEED_PENALTY = 20f; 1692 tooltip.addPara("Reduces top speed of ships by up to %s, and the top speed and range " 1693 + "of missiles by up to %s.", pad, 1694 highlight, 1695 "" + (int) Math.round(BattleCreationPluginImpl.ABYSS_SHIP_SPEED_PENALTY) + "%", 1696 "" + (int) Math.round(BattleCreationPluginImpl.ABYSS_MISSILE_SPEED_PENALTY) + "%" 1697 ); 1698 } else { 1699 tooltip.addPara("No combat effects.", nextPad); 1700 } 1701 } 1702 1703 tooltip.addPara("*1 light-year = 2000 units = 1 map grid cell", gray, pad); 1704 } 1705 1706 protected float getAdjustedSpeedMult(CampaignFleetAPI fleet, float baseMult) { 1707 float skillMod = fleet.getCommanderStats().getDynamic().getValue(Stats.NAVIGATION_PENALTY_MULT); 1708 if (skillMod < 0) skillMod = 0; 1709 if (skillMod > 1) skillMod = 1; 1710 1711 float penalty = 1f - baseMult; 1712 penalty *= skillMod; 1713 1714 return 1f - penalty; 1715 } 1716 1717 public boolean isTooltipExpandable() { 1718// CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 1719// boolean inCloud = isInClouds(player); 1720// return inCloud; 1721 return true; 1722 } 1723 1724 public float getTooltipWidth() { 1725 return 375f; 1726 } 1727 1728 public String getTerrainName() { 1729 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 1730 boolean inCloud = isInClouds(player); 1731 boolean inAbyss = isInAbyss(player); 1732 int [] tile = getTilePreferStorm(player.getLocation(), player.getRadius()); 1733 int val = 0; 1734 CellStateTracker cell = null; 1735 if (tile != null) { 1736 cell = activeCells[tile[0]][tile[1]]; 1737 } 1738 1739 String name = "Hyperspace"; 1740 if (inAbyss) { 1741 name = "Hyperspace (Abyssal)"; 1742 } else if (!inCloud) { 1743 } else if (cell == null || !cell.isStorming()) { 1744 name = "Hyperspace (Deep)"; 1745 } else if (cell.isStorming()) { 1746 name = "Hyperspace (Storm)"; 1747 } 1748 return name; 1749 } 1750 1751 1752 public String getEffectCategory() { 1753 return "dark-hyper-like"; 1754 } 1755 1756 public boolean hasAIFlag(Object flag) { 1757 return flag == TerrainAIFlags.REDUCES_SENSOR_RANGE; 1758 } 1759 1760 public boolean hasAIFlag(Object flag, CampaignFleetAPI fleet) { 1761 if (flag == TerrainAIFlags.DANGEROUS_UNLESS_GO_SLOW) { 1762 int [] tile = getTilePreferStorm(fleet.getLocation(), fleet.getRadius() + 100f); 1763 CellStateTracker cell = null; 1764 if (tile != null) { 1765 cell = activeCells[tile[0]][tile[1]]; 1766 } 1767 if (cell != null) { 1768 return cell.isStorming() || cell.isSignaling(); 1769 } 1770 } 1771 return hasAIFlag(flag); 1772 } 1773 1774 @Override 1775 public int getNumMapSamples() { 1776 return 10; 1777 } 1778 1779 public boolean isUseSampleCache() { 1780 return true; 1781 } 1782 1783 1784// public static float getBurnPenalty(CampaignFleetAPI fleet) { 1785// AsteroidBeltTerrainPlugin.getFleetRadiusTerrainEffectMult(fleet); 1786// 1787// float min = Global.getSettings().getBaseFleetSelectionRadius() + Global.getSettings().getFleetSelectionRadiusPerUnitSize(); 1788// float max = Global.getSettings().getMaxFleetSelectionRadius(); 1789// float radius = fleet.getRadius(); 1790// 1791// float penalty = 1f - (radius - min) / (max - min); 1792// if (penalty > 1) penalty = 1; 1793// if (penalty < 0) penalty = 0; 1794// penalty = MIN_BURN_PENALTY + penalty * BURN_PENALTY_RANGE; 1795// 1796// float skillMod = fleet.getCommanderStats().getDynamic().getValue(Stats.NAVIGATION_PENALTY_MULT); 1797// penalty *= skillMod; 1798// 1799// return penalty; 1800// } 1801 1802 public static void main(String[] args) { 1803 System.out.println(1.5f - (int) 1.5f); 1804 } 1805 1806 1807 public void turnOffStorms(Vector2f loc, float radius) { 1808 setTileState(loc, radius, CellState.OFF, -1f, -1f); 1809 } 1810 1811 public void setTileState(Vector2f loc, float radius, CellState state, float waitDur, float signalDur) { 1812 setTileState(loc, radius, state, waitDur, signalDur, signalDur); 1813 } 1814 public void setTileState(Vector2f loc, float radius, CellState state, float waitDur, float minSignalDur, float maxSignalDur) { 1815 float x = this.entity.getLocation().x; 1816 float y = this.entity.getLocation().y; 1817 float size = getTileSize(); 1818 float containsSize = getTileContainsSize(); 1819 1820 float w = tiles.length * size; 1821 float h = tiles[0].length * size; 1822 1823 x -= w/2f; 1824 y -= h/2f; 1825 1826 float extra = (containsSize - size) / 2f; 1827 1828 if (loc.x + radius + extra < x) return; 1829 if (loc.y + radius + extra < y) return; 1830 if (loc.x > x + w + radius + extra) return; 1831 if (loc.y > y + h + radius + extra) return; 1832 1833 int xMin = (int) ((loc.x - x - radius) / size); 1834 int yMin = (int) ((loc.y - y - radius) / size); 1835 int xMax = (int) ((loc.x - x + radius) / size); 1836 int yMax = (int) ((loc.y - y + radius) / size); 1837 1838 if (xMin < 0) xMin = 0; 1839 if (yMin < 0) yMin = 0; 1840 if (xMin >= tiles.length) xMin = tiles.length - 1; 1841 if (yMin >= tiles[0].length) yMin= tiles[0].length - 1; 1842 1843 if (xMax < 0) xMax = 0; 1844 if (yMax < 0) yMax = 0; 1845 if (xMax >= tiles.length) xMax = tiles.length - 1; 1846 if (yMax >= tiles[0].length) yMax = tiles[0].length - 1; 1847 1848 for (int i = xMin; i <= xMax; i++) { 1849 for (int j = yMin; j <= yMax; j++) { 1850 int texIndex = tiles[i][j]; 1851 if (texIndex >= 0) { 1852 float tx = x + i * size + size/2f - containsSize/2f; 1853 float ty = y + j * size + size/2f - containsSize/2f; 1854 1855 float dist = Misc.getDistance(loc.x, loc.y, tx, ty); 1856 if (dist > radius) continue; 1857// if (loc.x + radius < tx) continue; 1858// if (loc.y + radius < ty) continue; 1859// if (loc.x > tx + containsSize + radius) continue; 1860// if (loc.y > ty + containsSize + radius) continue; 1861 // 1862 CellStateTracker cell = activeCells[i][j]; 1863 float interval = auto.getInterval().getIntervalDuration(); 1864 float wait = interval * 0f + interval * 1.5f * (float) Math.random(); 1865 if (waitDur >= 0f) wait = waitDur; 1866 float signal = interval * 0.5f + interval * 0.5f * (float) Math.random(); 1867 if (minSignalDur >= 0f) signal = minSignalDur + (maxSignalDur - minSignalDur) * (float) Math.random(); 1868 1869 wait *= 0.9f + (float) Math.random() * 0.2f; 1870 signal *= 0.9f + (float) Math.random() * 0.2f; 1871 1872 if (cell == null && state != CellState.OFF) { 1873 cell = activeCells[i][j] = new CellStateTracker(i, j, wait, signal); 1874 cell.state = state; 1875 } else if (cell != null && state == CellState.OFF) { 1876 if (cell.state == CellState.STORM || 1877 cell.state == CellState.STORM_WANE || 1878 cell.state == CellState.SIGNAL) { 1879 float dur = 0.1f; 1880 if (wait >= 0f) { 1881 dur *= wait; 1882 } 1883 if (cell.state == CellState.STORM_WANE) { 1884 dur = Math.min(cell.wane, dur); 1885 } else { 1886 cell.maxWane = dur * 4f; 1887 } 1888 cell.wane = dur; 1889 cell.state = CellState.STORM_WANE; 1890 } else { 1891 activeCells[i][j] = null; 1892 } 1893 } else if (cell != null) { 1894 cell.state = state; 1895 if (state == CellState.WAIT) { 1896 cell.wait = wait; 1897 } 1898 if (state == CellState.SIGNAL) { 1899 //signal = Math.min(cell.signal, signal); 1900 cell.signal = signal; 1901 cell.maxSignal = signal; 1902 } 1903 } 1904 1905 } 1906 } 1907 } 1908 } 1909} 1910 1911 1912 1913 1914 1915 1916 1917