001package com.fs.starfarer.api.impl.combat;
002
003import java.awt.Color;
004
005import org.lwjgl.util.vector.Vector2f;
006
007import com.fs.starfarer.api.Global;
008import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
009import com.fs.starfarer.api.combat.CombatEngineAPI;
010import com.fs.starfarer.api.util.Misc;
011
012/**
013 * Used for ship explosions when there's no whiteout.
014 * 
015 * @author Alex
016 *
017 * Copyright 2022 Fractal Softworks, LLC
018 */
019public class ShockwaveVisual extends BaseCombatLayeredRenderingPlugin {
020
021        public static class ShockwaveParams implements Cloneable {
022                public Color color;
023                public float radius;
024                public Vector2f loc;
025                
026                public ShockwaveParams() {
027                }
028
029                public ShockwaveParams(Color color, float radius, Vector2f loc) {
030                        super();
031                        this.color = color;
032                        this.radius = radius;
033                        this.loc = loc;
034                }
035                
036                @Override
037                protected ShockwaveParams clone() {
038                        try {
039                                return (ShockwaveParams) super.clone();
040                        } catch (CloneNotSupportedException e) {
041                                return null; // should never happen
042                        }
043                }
044                
045        }
046        
047        
048        protected ShockwaveParams p;
049
050        
051        public static void spawnShockwave(ShockwaveParams params) {
052                CombatEngineAPI engine = Global.getCombatEngine();
053
054                float baseSize = params.radius * 0.08f;
055                float maxParticleSize = baseSize * 2f;
056                
057                float fullArea = (float) (Math.PI * params.radius * params.radius);
058                float particleArea = (float) (Math.PI * baseSize * baseSize);
059                
060                int count = (int) Math.round(fullArea / particleArea * 1f);
061                count *= 2;
062                if (count > 300) count = 300;
063                
064                float durMult = 2f;
065                //durMult = params.durationMult;
066
067                float maxSpeed = params.radius * 2f;
068                
069                Color negativeColor = new Color(0, 0, 0, 255);
070                
071                //baseSize *= 0.5f;
072                for (int i = 0; i < count; i++) {
073                        float size = baseSize * (1f + (float) Math.random());
074                        
075                        Color randomColor = new Color(Misc.random.nextInt(256), 
076                                                Misc.random.nextInt(256), Misc.random.nextInt(256), params.color.getAlpha());                   
077                        Color adjustedColor = Misc.interpolateColor(params.color, randomColor, 0.2f);
078                        adjustedColor = params.color;
079                        //adjustedColor = Misc.setAlpha(adjustedColor, 50);
080//                      ParticleData data = new ParticleData(adjustedColor, size, 
081//                                              (0.25f + (float) Math.random()) * 2f * durMult, 3f);
082                        
083                        float r = (float) Math.random();
084                        float dist = params.radius * 0.2f * (0.1f + r * 0.9f);
085                        float dir = (float) Math.random() * 360f;
086                        //data.setOffset(dir, dist, dist);
087                        Vector2f offset = Misc.getUnitVectorAtDegreeAngle(dir);
088                        //offset.scale(dist + (dist - dist) * (float) Math.random());
089                        offset.scale(dist);
090                        
091//                      dir = Misc.getAngleInDegrees(data.offset);
092//                      data.setVelocity(dir, baseSize * 0.25f, baseSize * 0.5f);
093//                      data.vel.scale(1f / durMult);
094                        
095//                      data.swImpact = (float) Math.random();
096//                      if (i > count / 2) data.swImpact = 1;
097                        
098                        Vector2f loc = Vector2f.add(params.loc, offset, new Vector2f());
099                        
100                        float speed = maxSpeed * (0.25f + 0.75f * (float) Math.random());
101                        Vector2f vel = Misc.getUnitVectorAtDegreeAngle(dir);
102                        vel.scale(speed);
103                        
104                        float rampUp = 0f;
105                        float dur = 1f;
106                        
107                        float mult = 0.33f;
108                        mult = 1f;
109                        mult = 0.5f;
110                        rampUp *= mult;
111                        dur *= mult;
112                        
113                        engine.addNebulaParticle(loc, vel, size, 3f, rampUp, 0f, dur, adjustedColor);   
114                        //engine.addNegativeNebulaParticle(loc, vel, size, 3f, rampUp, 0f, dur, negativeColor); 
115                        //engine.addNebulaSmokeParticle(loc, vel, size, 3f, rampUp, 0f, dur, negativeColor);    
116                }
117                
118                
119        }
120        
121        /*
122        public ShockwaveVisual(ShockwaveParams p) {
123                this.p = p;
124        }
125        
126        public float getRenderRadius() {
127                return p.radius + 500f;
128        }
129        
130        
131        @Override
132        public EnumSet<CombatEngineLayers> getActiveLayers() {
133                return EnumSet.of(CombatEngineLayers.ABOVE_PARTICLES_LOWER);
134        }
135
136        public void advance(float amount) {
137                if (Global.getCombatEngine().isPaused()) return;
138                
139                
140                CombatEngineAPI engine = Global.getCombatEngine();
141                engine.addNebulaParticle(loc, entity.getVelocity(), s, 1.5f, rampUp, 0f, dur, c);
142        }
143
144        public void init(CombatEntityAPI entity) {
145                super.init(entity);
146        }
147
148        public boolean isExpired() {
149                return false;
150        }
151
152        public void render(CombatEngineLayers layer, ViewportAPI viewport) {
153                float x = entity.getLocation().x;
154                float y = entity.getLocation().y;
155        
156                float f = fader.getBrightness();
157                float alphaMult = viewport.getAlphaMult();
158                if (f < 0.5f) {
159                        alphaMult *= f * 2f;
160                }
161                
162                float r = p.radius;
163                float tSmall = p.thickness;
164                
165                if (fader.isFadingIn()) {
166                        r *= 0.75f + Math.sqrt(f) * 0.25f;
167                } else {
168                        r *= 0.1f + 0.9f * f;
169                        tSmall = Math.min(r * 1f, p.thickness);
170                }
171                
172//              GL11.glPushMatrix();
173//              GL11.glTranslatef(x, y, 0);
174//              GL11.glScalef(6f, 6f, 1f);
175//              x = y = 0;
176
177                //GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
178                if (layer == CombatEngineLayers.ABOVE_PARTICLES_LOWER) {
179                        float a = 1f;
180                        renderAtmosphere(x, y, r, tSmall, alphaMult * a, segments, atmosphereTex, noise, p.color, true);
181                        renderAtmosphere(x, y, r - 2f, tSmall, alphaMult * a, segments, atmosphereTex, noise, p.color, true);
182                } else if (layer == CombatEngineLayers.ABOVE_PARTICLES) {
183                        float circleAlpha = 1f;
184                        if (alphaMult < 0.5f) {
185                                circleAlpha = alphaMult * 2f;
186                        }
187                        float tCircleBorder = 1f;
188                        renderCircle(x, y, r, circleAlpha, segments, Color.black);
189                        renderAtmosphere(x, y, r, tCircleBorder, circleAlpha, segments, atmosphereTex, noise, Color.black, false);
190                }
191                //GL14.glBlendEquation(GL14.GL_FUNC_ADD);
192                
193//              GL11.glPopMatrix();
194        }
195
196        
197        private void renderCircle(float x, float y, float radius, float alphaMult, int segments, Color color) {
198                if (fader.isFadingIn()) alphaMult = 1f;
199                
200                float startRad = (float) Math.toRadians(0);
201                float endRad = (float) Math.toRadians(360);
202                float spanRad = Misc.normalizeAngle(endRad - startRad);
203                float anglePerSegment = spanRad / segments;
204                
205                GL11.glPushMatrix();
206                GL11.glTranslatef(x, y, 0);
207                GL11.glRotatef(0, 0, 0, 1);
208                GL11.glDisable(GL11.GL_TEXTURE_2D);
209                
210                GL11.glEnable(GL11.GL_BLEND);
211                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
212                
213                
214                GL11.glColor4ub((byte)color.getRed(),
215                                                (byte)color.getGreen(),
216                                                (byte)color.getBlue(),
217                                                (byte)((float) color.getAlpha() * alphaMult));
218                
219                GL11.glBegin(GL11.GL_TRIANGLE_FAN);
220                GL11.glVertex2f(0, 0);
221                for (float i = 0; i < segments + 1; i++) {
222                        boolean last = i == segments;
223                        if (last) i = 0;
224                        float theta = anglePerSegment * i;
225                        float cos = (float) Math.cos(theta);
226                        float sin = (float) Math.sin(theta);
227                        
228                        float m1 = 0.75f + 0.65f * noise[(int)i];
229                        if (p.noiseMag <= 0) {
230                                m1 = 1f;
231                        }
232                        
233                        float x1 = cos * radius * m1;
234                        float y1 = sin * radius * m1;
235                        
236                        GL11.glVertex2f(x1, y1);
237                        
238                        if (last) break;
239                }
240                
241                
242                GL11.glEnd();
243                GL11.glPopMatrix();
244                
245        }
246        
247        
248        private void renderAtmosphere(float x, float y, float radius, float thickness, float alphaMult, int segments, SpriteAPI tex, float [] noise, Color color, boolean additive) {
249                
250                float startRad = (float) Math.toRadians(0);
251                float endRad = (float) Math.toRadians(360);
252                float spanRad = Misc.normalizeAngle(endRad - startRad);
253                float anglePerSegment = spanRad / segments;
254                
255                GL11.glPushMatrix();
256                GL11.glTranslatef(x, y, 0);
257                GL11.glRotatef(0, 0, 0, 1);
258                GL11.glEnable(GL11.GL_TEXTURE_2D);
259                
260                tex.bindTexture();
261
262                GL11.glEnable(GL11.GL_BLEND);
263                if (additive) {
264                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
265                } else {
266                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
267                }
268                
269                GL11.glColor4ub((byte)color.getRed(),
270                                                (byte)color.getGreen(),
271                                                (byte)color.getBlue(),
272                                                (byte)((float) color.getAlpha() * alphaMult));
273                float texX = 0f;
274                float incr = 1f / segments;
275                GL11.glBegin(GL11.GL_QUAD_STRIP);
276                for (float i = 0; i < segments + 1; i++) {
277                        boolean last = i == segments;
278                        if (last) i = 0;
279                        float theta = anglePerSegment * i;
280                        float cos = (float) Math.cos(theta);
281                        float sin = (float) Math.sin(theta);
282                        
283                        float m1 = 0.75f + 0.65f * noise[(int)i];
284                        float m2 = m1;
285                        if (p.noiseMag <= 0) {
286                                m1 = 1f;
287                                m2 = 1f;
288                        }
289                        
290                        float x1 = cos * radius * m1;
291                        float y1 = sin * radius * m1;
292                        float x2 = cos * (radius + thickness * m2);
293                        float y2 = sin * (radius + thickness * m2);
294                        
295                        GL11.glTexCoord2f(0.5f, 0.05f);
296                        GL11.glVertex2f(x1, y1);
297                        
298                        GL11.glTexCoord2f(0.5f, 0.95f);
299                        GL11.glVertex2f(x2, y2);
300                        
301                        texX += incr;
302                        if (last) break;
303                }
304                
305                GL11.glEnd();
306                GL11.glPopMatrix();
307        }
308        */
309}
310
311