Starsector API
Loading...
Searching...
No Matches
HyperspaceTerrainPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.terrain;
2
3import java.util.ArrayList;
4import java.util.EnumSet;
5import java.util.List;
6import java.util.Random;
7
8import java.awt.Color;
9
10import org.lwjgl.opengl.GL11;
11import org.lwjgl.util.vector.Vector2f;
12
13import com.fs.starfarer.api.Global;
14import com.fs.starfarer.api.campaign.CampaignEngineLayers;
15import com.fs.starfarer.api.campaign.CampaignFleetAPI;
16import com.fs.starfarer.api.campaign.SectorEntityToken;
17import com.fs.starfarer.api.campaign.StarSystemAPI;
18import com.fs.starfarer.api.campaign.TerrainAIFlags;
19import com.fs.starfarer.api.campaign.rules.MemoryAPI;
20import com.fs.starfarer.api.combat.ViewportAPI;
21import com.fs.starfarer.api.fleet.FleetMemberAPI;
22import com.fs.starfarer.api.fleet.FleetMemberViewAPI;
23import com.fs.starfarer.api.graphics.SpriteAPI;
24import com.fs.starfarer.api.impl.campaign.abilities.EmergencyBurnAbility;
25import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
26import com.fs.starfarer.api.impl.campaign.ids.Stats;
27import com.fs.starfarer.api.impl.campaign.ids.Tags;
28import com.fs.starfarer.api.impl.combat.BattleCreationPluginImpl;
29import com.fs.starfarer.api.loading.Description.Type;
30import com.fs.starfarer.api.ui.Alignment;
31import com.fs.starfarer.api.ui.TooltipMakerAPI;
32import com.fs.starfarer.api.util.FlickerUtilV2;
33import com.fs.starfarer.api.util.Misc;
34import com.fs.starfarer.api.util.WeightedRandomPicker;
35
36public class HyperspaceTerrainPlugin extends BaseTiledTerrain { // implements NebulaTextureProvider {
37
38 public static float ABYSS_MUSIC_SUPPRESSION = 1f;
39
40// public static class AbyssalMusicDamper implements EveryFrameScript {
41// public boolean isDone() {
42// return false;
43// }
44// public boolean runWhilePaused() {
45// return true;
46// }
47//
48// public void advance(float amount) {
49//
50// }
51// }
52
53
54 public static float ABYSS_VISIBLITY_MULT = 0.25f;
55 public static float ABYSS_SENSOR_RANGE_MULT = 0.25f;
56 public static float ABYSS_BURN_MULT = 0.25f;
57
58 public static float ABYSS_NAVIGATION_EFFECT = 0;
59
60
61 public static Color ABYSS_BACKGROUND_COLOR = new Color(0, 0, 0, 255);
62 public static Color ABYSS_PARTICLE_COLOR = new Color(0, 0, 0, 0);
63 public static Color ABYSS_LIGHT_COLOR = new Color(170, 170, 170, 255);
64
65
66
67 public static String STORM_STRIKE_TIMEOUT_KEY = "$stormStrikeTimeout";
68
69 public static float VISIBLITY_MULT = 0.5f;
70
71
72 public static float STORM_STRIKE_SOUND_RANGE = 1500f;
73
74 public static float STORM_MIN_TIMEOUT = 0.4f;
75 public static float STORM_MAX_TIMEOUT = 0.6f;
76 public static float STORM_DAMAGE_FRACTION = 0.3f;
77 public static float STORM_MIN_STRIKE_DAMAGE = 0.05f;
78 public static float STORM_MAX_STRIKE_DAMAGE = 0.95f;
79
80 public static float STORM_SPEED_MULT = 1f;
81 public static float STORM_SENSOR_RANGE_MULT = 1f;
82 public static float STORM_VISIBILITY_FLAT = 0f;
83
84
85 public static float TILE_SIZE = 200;
86
91 //public static final CampaignEngineLayers OVER = CampaignEngineLayers.TERRAIN_6B;
94
95 public static enum LocationState {
96 OPEN,
97 DEEP,
98 DEEP_STORM,
99 }
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
219
220 protected transient String stormSoundId = null;
222
224
225 public void init(String terrainId, SectorEntityToken entity, Object param) {
226 super.init(terrainId, entity, param);
227 }
228
230 if (abyssPlugin == null) {
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) {
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) {
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
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();
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;
685
687 this.stormCellTimeMultOutsideBaseArea = stormCellTimeMultOutsideBaseArea;
688 }
689 protected transient float extraDistanceAroundPlayerToAdvanceStormCells = 0f;
693
697
698
699 public void advance(float amount) {
700 //if (true) return;
701 super.advance(amount);
702
703 getAbyssPlugin().advance(amount);
704
708 }
709
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
733 Vector2f test = new Vector2f();
734 if (playerFleet != null) {
735 test = playerFleet.getLocationInHyperspace();
736 }
737
738 if (entity.getContainingLocation() != null &&
740 isInAbyss(playerFleet)) {
741
742 float depth = getAbyssalDepth(playerFleet);
744 "abyss_color", ABYSS_BACKGROUND_COLOR, 1f, 1f, depth);
745
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) {
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;
835 if (i < baseMinX || j < baseMinY || i > baseMaxX || j > baseMaxY) {
837 }
838 }
839 curr.advance(days * timeMult);
840 if (curr.isOff()) {
841 activeCells[i][j] = null;
842 }
843 }
844 }
845 }
846
849 }
850
851
853 if (stormSoundId == null) return;
854
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);
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) {
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
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 }
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() {
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() {
1328 return super.shouldPlayLoopThree() && isInAbyss(playerFleet);
1329 }
1330
1331 @Override
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) {
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);
1364
1365 if (entity instanceof CampaignFleetAPI) {
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) {
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;
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,
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,
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
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 }
1559 member.getShipName() + " " + verb + " damage from the storm", c);
1560
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
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
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
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) {
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() {
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) {
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
static SettingsAPI getSettings()
Definition Global.java:57
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static SectorAPI getSector()
Definition Global.java:65
float computeEffective(float baseValue)
static float getCRCost(FleetMemberAPI member, CampaignFleetAPI fleet)
static final String NAVIGATION_PENALTY_MULT
Definition Stats.java:77
boolean isPreventedFromAffecting(SectorEntityToken other)
LocationState getStateAt(SectorEntityToken entity, float extraRadius)
void setTileState(Vector2f loc, float radius, CellState state, float waitDur, float minSignalDur, float maxSignalDur)
void renderOnRadar(Vector2f radarCenter, float factor, float alphaMult)
void render(CampaignEngineLayers layer, ViewportAPI viewport)
void renderQuad(int i, int j, float x, float y, float width, float height, float texX, float texY, float texW, float texH, float angle)
void setStormCellTimeMultOutsideBaseArea(float stormCellTimeMultOutsideBaseArea)
void renderSubArea(float startColumn, float endColumn, float startRow, float endRow, float factor, int samples, float alphaMult)
void setExtraDistanceAroundPlayerToAdvanceStormCells(float extraDistanceAroundPlayerToAdvanceStormCells)
void applyStormStrikes(CellStateTracker cell, CampaignFleetAPI fleet, float days)
void setTileState(Vector2f loc, float radius, CellState state, float waitDur, float signalDur)
void init(String terrainId, SectorEntityToken entity, Object param)
CellStateTracker getCellAt(SectorEntityToken entity, float extraRadius)
static float getBurnMultForTerrain(CampaignFleetAPI fleet)
Definition Misc.java:4976
static Color getTextColor()
Definition Misc.java:839
static Color getNegativeHighlightColor()
Definition Misc.java:802
static final Vector2f ZERO
Definition Misc.java:249
static Color getGrayColor()
Definition Misc.java:826
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static Color getHighlightColor()
Definition Misc.java:792
static Color scaleColorOnly(Color color, float factor)
Definition Misc.java:1302
static Color interpolateColor(Color from, Color to, float progress)
Definition Misc.java:1261
static boolean isSlowMoving(CampaignFleetAPI fleet)
Definition Misc.java:5651
static String getRoundedValueMaxOneAfterDecimal(float value)
Definition Misc.java:673
Description getDescription(String id, Type type)
String getSpriteName(String category, String id)
SpriteAPI getSprite(String filename)
void applyLowPassFilter(float gain, float gainHF)
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
List< FleetMemberViewAPI > getViews()
MutableCharacterStatsAPI getCommanderStats()
List< FleetMemberAPI > getMembersListCopy()
ColorShifterAPI getBackgroundParticleColorShifter()
SectorEntityToken createToken(float x, float y)
void addScript(EveryFrameScript script)
void set(String key, Object value)
void addTemporaryModMult(float durInDays, String source, String desc, float value, StatBonus stat)
void addTemporaryModFlat(float durInDays, String source, float value, StatBonus stat)
void applyCREvent(float crChange, String description)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
LabelAPI addSectionHeading(String str, Alignment align, float pad)
void shift(Object source, Color to, float durIn, float durOut, float shift)