Starsector API
Loading...
Searching...
No Matches
NegativeExplosionVisual.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.awt.Color;
4import java.util.Arrays;
5import java.util.EnumSet;
6
7import org.lwjgl.opengl.GL11;
8import org.lwjgl.util.vector.Vector2f;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
12import com.fs.starfarer.api.combat.CombatEngineAPI;
13import com.fs.starfarer.api.combat.CombatEngineLayers;
14import com.fs.starfarer.api.combat.CombatEntityAPI;
15import com.fs.starfarer.api.combat.ViewportAPI;
16import com.fs.starfarer.api.graphics.SpriteAPI;
17import com.fs.starfarer.api.util.FaderUtil;
18import com.fs.starfarer.api.util.Misc;
19import com.fs.starfarer.api.util.Noise;
20
21public class NegativeExplosionVisual extends BaseCombatLayeredRenderingPlugin {
22
23 public static class NEParams implements Cloneable {
24 //public float fadeIn = 0.25f;
25 public float fadeIn = 0.1f;
26 public float fadeOut = 0.5f;
27 public float spawnHitGlowAt = 0f;
28 public float hitGlowSizeMult = 0.75f;
29 public float radius = 20f;
30 public float thickness = 25f;
31 public float noiseMag = 1f;
32 public float noisePeriod = 0.1f;
33 public boolean withHitGlow = true;
34 public Color color = new Color(100,100,255);
35 public Color underglow = RiftCascadeEffect.EXPLOSION_UNDERCOLOR;
36 public Color invertForDarkening = null;
37
38 public NEParams() {
39 }
40
41 public NEParams(float radius, float thickness, Color color) {
42 super();
43 this.radius = radius;
44 this.thickness = thickness;
45 this.color = color;
46 }
47
48 @Override
49 protected NEParams clone() {
50 try {
51 return (NEParams) super.clone();
52 } catch (CloneNotSupportedException e) {
53 return null; // should never happen
54 }
55 }
56
57 }
58
59 protected FaderUtil fader;
60 protected SpriteAPI atmosphereTex;
61
62 protected float [] noise;
63 protected float [] noise1;
64 protected float [] noise2;
65
66// protected float radius;
67// protected float thickness;
68// protected Color color;
69// protected float noiseMag;
70// protected float noisePeriod;
71
72 protected NEParams p;
73
74 protected int segments;
75 protected float noiseElapsed = 0f;
76
77 protected boolean spawnedHitGlow = false;
78
79 public NegativeExplosionVisual(NEParams p) {
80 this.p = p;
81 }
82
83 public float getRenderRadius() {
84 return p.radius + 500f;
85 }
86
87
88 @Override
89 public EnumSet<CombatEngineLayers> getActiveLayers() {
90 return EnumSet.of(CombatEngineLayers.ABOVE_PARTICLES_LOWER, CombatEngineLayers.ABOVE_PARTICLES);
91 }
92
93 public void advance(float amount) {
94 if (Global.getCombatEngine().isPaused()) return;
95
96 fader.advance(amount);
97
98 if (p.noiseMag > 0) {
99 noiseElapsed += amount;
100 if (noiseElapsed > p.noisePeriod) {
101 noiseElapsed = 0;
102 noise1 = Arrays.copyOf(noise2, noise2.length);
103 noise2 = Noise.genNoise(segments, p.noiseMag);
104 }
105 float f = noiseElapsed / p.noisePeriod;
106 for (int i = 0; i < noise.length; i++) {
107 float n1 = noise1[i];
108 float n2 = noise2[i];
109 noise[i] = n1 + (n2 - n1) * f;
110 }
111 }
112
113 if (!p.withHitGlow) return;
114
115 float glowSpawnAt = 1f;
116 if (!spawnedHitGlow && (!fader.isFadingIn() || fader.getBrightness() >= p.spawnHitGlowAt)) {
117 float size = Math.min(p.radius * 7f, p.radius + 150f);
118 float coreSize = Math.max(size, p.radius * 4f);
119 if (coreSize > size) size = coreSize;
120
121 size *= p.hitGlowSizeMult;
122 coreSize *= p.hitGlowSizeMult;
123
124 CombatEngineAPI engine = Global.getCombatEngine();
125 Vector2f point = entity.getLocation();
126 Vector2f vel = entity.getVelocity();
127 float dur = fader.getDurationOut() * glowSpawnAt;
128 engine.addHitParticle(point, vel, size * 3f, 1f, dur, p.color);
129 //engine.addHitParticle(point, vel, size * 3.0f, 1f, dur, p.color);
130 engine.addHitParticle(point, vel, coreSize * 1.5f, 1f, dur, Color.white);
131 //engine.addHitParticle(point, vel, coreSize * 1f, 1f, dur, Color.white);
132
133 Color invert = p.color;
134 if (p.invertForDarkening != null) invert = p.invertForDarkening;
135 Color c = new Color(255 - invert.getRed(),
136 255 - invert.getGreen(),
137 255 - invert.getBlue(), 127);
138 c = Misc.interpolateColor(c, Color.white, 0.4f);
139 //c = Misc.setAlpha(c, 80);
140 //c = Misc.scaleAlpha(c, 0.5f);
141 float durMult = 1f;
142 for (int i = 0; i < 7; i++) {
143 dur = 4f + 4f * (float) Math.random();
144 //dur = p.fadeIn + p.fadeOut + 3f + (float) Math.random() * 2f;
145 dur *= durMult;
146 dur *= 0.5f;
147 //float nSize = size * (1f + 0.0f * (float) Math.random());
148 //float nSize = size * (0.75f + 0.5f * (float) Math.random());
149 float nSize = size * 1f;
150 Vector2f pt = Misc.getPointAtRadius(point, nSize * 0.5f);
151 Vector2f v = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f);
152 v.scale(nSize + nSize * (float) Math.random() * 0.5f);
153 v.scale(0.15f);
154 Vector2f.add(vel, v, v);
155
156// float maxSpeed = nSize * 1.5f * 0.2f;
157// float minSpeed = nSize * 1f * 0.2f;
158// float overMin = v.length() - minSpeed;
159// if (overMin > 0) {
160// float durMult2 = 1f - overMin / (maxSpeed - minSpeed);
161// if (durMult2 < 0.1f) durMult2 = 0.1f;
162// dur *= 0.5f + 0.5f * durMult2;
163// }
164 v = new Vector2f(entity.getVelocity());
165// engine.addNegativeParticle(pt, v, nSize * 1f, p.fadeIn / dur, dur, c);
166 engine.addNegativeNebulaParticle(pt, v, nSize * 1f, 2f,
167 p.fadeIn / dur, 0f, dur, c);
168 }
169
170 dur = p.fadeIn + p.fadeOut + 2f;
171 dur *= durMult;
172 float rampUp = (p.fadeIn + p.fadeOut) / dur;
173 rampUp = 0f;
174 //rampUp = rampUp + (1f - rampUp) * 0.25f;
175 //float sizeMult = p.hitGlowSizeMult;
176
177 c = p.underglow;
178 //c = Misc.setAlpha(c, 255);
179 for (int i = 0; i < 15; i++) {
180 //rampUp = (float) Math.random() * 0.05f + 0.05f;
181 Vector2f loc = new Vector2f(point);
182 loc = Misc.getPointWithinRadius(loc, size * 1f);
183 //loc = Misc.getPointAtRadius(loc, size * 1f);
184 float s = size * 3f * (0.25f + (float) Math.random() * 0.25f);
185 //s *= 0.5f;
186// engine.addSmoothParticle(loc, entity.getVelocity(), s, 1f, rampUp, dur, c);
187 engine.addNebulaParticle(loc, entity.getVelocity(), s, 1.5f, rampUp, 0f, dur, c);
188 }
189 spawnedHitGlow = true;
190 }
191 }
192
193 public void init(CombatEntityAPI entity) {
194 super.init(entity);
195
196 fader = new FaderUtil(0f, p.fadeIn, p.fadeOut);
197 fader.setBounceDown(true);
198 fader.fadeIn();
199
200 atmosphereTex = Global.getSettings().getSprite("combat", "corona_hard");
201
202 float perSegment = 2f;
203 segments = (int) ((p.radius * 2f * 3.14f) / perSegment);
204 if (segments < 8) segments = 8;
205
206 noise1 = Noise.genNoise(segments, p.noiseMag);
207 noise2 = Noise.genNoise(segments, p.noiseMag);
208 noise = Arrays.copyOf(noise1, noise1.length);
209 }
210
211 public boolean isExpired() {
212 return fader.isFadedOut();
213 }
214
215 public void render(CombatEngineLayers layer, ViewportAPI viewport) {
216 float x = entity.getLocation().x;
217 float y = entity.getLocation().y;
218
219 float f = fader.getBrightness();
220 float alphaMult = viewport.getAlphaMult();
221 if (f < 0.5f) {
222 alphaMult *= f * 2f;
223 }
224
225 float r = p.radius;
226 float tSmall = p.thickness;
227
228 if (fader.isFadingIn()) {
229 r *= 0.75f + Math.sqrt(f) * 0.25f;
230 } else {
231 r *= 0.1f + 0.9f * f;
232 tSmall = Math.min(r * 1f, p.thickness);
233 }
234
235// GL11.glPushMatrix();
236// GL11.glTranslatef(x, y, 0);
237// GL11.glScalef(6f, 6f, 1f);
238// x = y = 0;
239
240 //GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
241 if (layer == CombatEngineLayers.ABOVE_PARTICLES_LOWER) {
242 float a = 1f;
243 renderAtmosphere(x, y, r, tSmall, alphaMult * a, segments, atmosphereTex, noise, p.color, true);
244 renderAtmosphere(x, y, r - 2f, tSmall, alphaMult * a, segments, atmosphereTex, noise, p.color, true);
245 } else if (layer == CombatEngineLayers.ABOVE_PARTICLES) {
246 float circleAlpha = 1f;
247 if (alphaMult < 0.5f) {
248 circleAlpha = alphaMult * 2f;
249 }
250 float tCircleBorder = 1f;
251 renderCircle(x, y, r, circleAlpha, segments, Color.black);
252 renderAtmosphere(x, y, r, tCircleBorder, circleAlpha, segments, atmosphereTex, noise, Color.black, false);
253 }
254 //GL14.glBlendEquation(GL14.GL_FUNC_ADD);
255
256// GL11.glPopMatrix();
257 }
258
259
260 private void renderCircle(float x, float y, float radius, float alphaMult, int segments, Color color) {
261 if (fader.isFadingIn()) alphaMult = 1f;
262
263 float startRad = (float) Math.toRadians(0);
264 float endRad = (float) Math.toRadians(360);
265 float spanRad = Misc.normalizeAngle(endRad - startRad);
266 float anglePerSegment = spanRad / segments;
267
268 GL11.glPushMatrix();
269 GL11.glTranslatef(x, y, 0);
270 GL11.glRotatef(0, 0, 0, 1);
271 GL11.glDisable(GL11.GL_TEXTURE_2D);
272
273 GL11.glEnable(GL11.GL_BLEND);
274 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
275
276
277 GL11.glColor4ub((byte)color.getRed(),
278 (byte)color.getGreen(),
279 (byte)color.getBlue(),
280 (byte)((float) color.getAlpha() * alphaMult));
281
282 GL11.glBegin(GL11.GL_TRIANGLE_FAN);
283 GL11.glVertex2f(0, 0);
284 for (float i = 0; i < segments + 1; i++) {
285 boolean last = i == segments;
286 if (last) i = 0;
287 float theta = anglePerSegment * i;
288 float cos = (float) Math.cos(theta);
289 float sin = (float) Math.sin(theta);
290
291 float m1 = 0.75f + 0.65f * noise[(int)i];
292 if (p.noiseMag <= 0) {
293 m1 = 1f;
294 }
295
296 float x1 = cos * radius * m1;
297 float y1 = sin * radius * m1;
298
299 GL11.glVertex2f(x1, y1);
300
301 if (last) break;
302 }
303
304
305 GL11.glEnd();
306 GL11.glPopMatrix();
307
308 }
309
310
311 private void renderAtmosphere(float x, float y, float radius, float thickness, float alphaMult, int segments, SpriteAPI tex, float [] noise, Color color, boolean additive) {
312
313 float startRad = (float) Math.toRadians(0);
314 float endRad = (float) Math.toRadians(360);
315 float spanRad = Misc.normalizeAngle(endRad - startRad);
316 float anglePerSegment = spanRad / segments;
317
318 GL11.glPushMatrix();
319 GL11.glTranslatef(x, y, 0);
320 GL11.glRotatef(0, 0, 0, 1);
321 GL11.glEnable(GL11.GL_TEXTURE_2D);
322
323 tex.bindTexture();
324
325 GL11.glEnable(GL11.GL_BLEND);
326 if (additive) {
327 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
328 } else {
329 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
330 }
331
332 GL11.glColor4ub((byte)color.getRed(),
333 (byte)color.getGreen(),
334 (byte)color.getBlue(),
335 (byte)((float) color.getAlpha() * alphaMult));
336 float texX = 0f;
337 float incr = 1f / segments;
338 GL11.glBegin(GL11.GL_QUAD_STRIP);
339 for (float i = 0; i < segments + 1; i++) {
340 boolean last = i == segments;
341 if (last) i = 0;
342 float theta = anglePerSegment * i;
343 float cos = (float) Math.cos(theta);
344 float sin = (float) Math.sin(theta);
345
346 float m1 = 0.75f + 0.65f * noise[(int)i];
347 float m2 = m1;
348 if (p.noiseMag <= 0) {
349 m1 = 1f;
350 m2 = 1f;
351 }
352
353 float x1 = cos * radius * m1;
354 float y1 = sin * radius * m1;
355 float x2 = cos * (radius + thickness * m2);
356 float y2 = sin * (radius + thickness * m2);
357
358 GL11.glTexCoord2f(0.5f, 0.05f);
359 GL11.glVertex2f(x1, y1);
360
361 GL11.glTexCoord2f(0.5f, 0.95f);
362 GL11.glVertex2f(x2, y2);
363
364 texX += incr;
365 if (last) break;
366 }
367
368 GL11.glEnd();
369 GL11.glPopMatrix();
370 }
371}
372
373
static SettingsAPI getSettings()
Definition Global.java:51
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
void render(CombatEngineLayers layer, ViewportAPI viewport)
SpriteAPI getSprite(String filename)