001package com.fs.starfarer.api.impl.combat;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.EnumSet;
006import java.util.List;
007
008import org.lwjgl.opengl.GL14;
009import org.lwjgl.util.vector.Vector2f;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
013import com.fs.starfarer.api.combat.CombatEngineLayers;
014import com.fs.starfarer.api.combat.CombatEntityAPI;
015import com.fs.starfarer.api.combat.DamagingProjectileAPI;
016import com.fs.starfarer.api.combat.ViewportAPI;
017import com.fs.starfarer.api.graphics.SpriteAPI;
018import com.fs.starfarer.api.util.FaderUtil;
019import com.fs.starfarer.api.util.Misc;
020
021public class CombatEntityPluginWithParticles extends BaseCombatLayeredRenderingPlugin {
022        
023        public static class ParticleData {
024                public SpriteAPI sprite;
025                public Vector2f offset = new Vector2f();
026                public Vector2f vel = new Vector2f();
027                public float scale = 1f;
028                public float scaleIncreaseRate = 1f;
029                public float turnDir = 1f;
030                public float angle = 1f;
031                
032                public float maxDur;
033                public FaderUtil fader;
034                public float elapsed = 0f;
035                public float baseSize;
036                protected Color color;
037                
038                public ParticleData(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color, String spriteSheetKey) {
039                        this.color = color;
040                        if (spriteSheetKey == null) {
041                                spriteSheetKey = "nebula_particles";
042                        }
043                        sprite = Global.getSettings().getSprite("misc", spriteSheetKey);
044                        //sprite = Global.getSettings().getSprite("misc", "dust_particles");
045                        float i = Misc.random.nextInt(4);
046                        float j = Misc.random.nextInt(4);
047                        sprite.setTexWidth(0.25f);
048                        sprite.setTexHeight(0.25f);
049                        sprite.setTexX(i * 0.25f);
050                        sprite.setTexY(j * 0.25f);
051                        sprite.setAdditiveBlend();
052                        
053                        angle = (float) Math.random() * 360f;
054                        
055                        this.maxDur = durIn + durOut;
056                        scaleIncreaseRate = endSizeMult / maxDur;
057                        if (endSizeMult < 1f) {
058                                scaleIncreaseRate = -1f * endSizeMult;
059                        }
060                        scale = 1f;
061                        
062                        this.baseSize = baseSize;
063                        turnDir = Math.signum((float) Math.random() - 0.5f) * maxAngVel * (float) Math.random();
064                        //turnDir = 0f;
065                        
066                        float driftDir = (float) Math.random() * 360f;
067                        vel = Misc.getUnitVectorAtDegreeAngle(driftDir);
068                        //vel.scale(proj.getProjectileSpec().getLength() / maxDur * (0f + (float) Math.random() * 3f));
069                        vel.scale(maxDriftVel * (0.5f + (float) Math.random() * 0.5f));
070                        
071                        fader = new FaderUtil(0f, durIn, durOut);
072                        fader.setBounceDown(true);
073                        fader.forceOut();
074                        fader.fadeIn();
075                }
076                
077                public void advance(float amount) {
078                        scale += scaleIncreaseRate * amount;
079                        
080                        offset.x += vel.x * amount;
081                        offset.y += vel.y * amount;
082                                
083                        angle += turnDir * amount;
084                        
085                        elapsed += amount;
086//                      if (maxDur - elapsed <= fader.getDurationOut() + 0.1f) {
087//                              fader.fadeOut();
088//                      }
089                        fader.advance(amount);
090                }
091        }
092        
093        protected List<ParticleData> particles = new ArrayList<ParticleData>();
094        protected List<ParticleData> darkParticles = new ArrayList<ParticleData>();
095        protected EnumSet<CombatEngineLayers> layers = EnumSet.of(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER);
096        protected CombatEngineLayers normalLayer;
097        protected CombatEngineLayers darkLayer;
098        
099        public CombatEntityPluginWithParticles() {
100                this(CombatEngineLayers.ABOVE_PARTICLES_LOWER, CombatEngineLayers.ABOVE_PARTICLES);
101        }
102        public CombatEntityPluginWithParticles(CombatEngineLayers normalLayer, CombatEngineLayers darkLayer) {
103                this.normalLayer = normalLayer;
104                this.darkLayer = darkLayer;
105                layers = EnumSet.of(normalLayer, darkLayer);
106        }
107        
108        public void init(CombatEntityAPI entity) {
109                super.init(entity);
110        }
111
112        @Override
113        public EnumSet<CombatEngineLayers> getActiveLayers() {
114                return layers;
115        }
116        
117        protected ParticleData prev;
118
119        protected String spriteSheetKey;
120        protected String darkSpriteSheetKey;
121        public String getSpriteSheetKey() {
122                return spriteSheetKey;
123        }
124        public void setSpriteSheetKey(String spriteSheetKey) {
125                this.spriteSheetKey = spriteSheetKey;
126        }
127        public String getDarkSpriteSheetKey() {
128                return darkSpriteSheetKey;
129        }
130        public void setDarkSpriteSheetKey(String darkSpriteSheetKey) {
131                this.darkSpriteSheetKey = darkSpriteSheetKey;
132        }
133        
134        public ParticleData addParticle(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color) {
135                ParticleData p = new ParticleData(baseSize, durIn, durOut, endSizeMult, maxDriftVel, maxAngVel, color, spriteSheetKey);
136                particles.add(p);
137                prev = p;
138                return p;
139        }
140        public ParticleData addDarkParticle(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color) {
141                ParticleData p = new ParticleData(baseSize, durIn, durOut, endSizeMult, maxDriftVel, maxAngVel, color, darkSpriteSheetKey);
142                darkParticles.add(p);
143                prev = p;
144                return p;
145        }
146        
147        public void randomizePrevParticleLocation(float maxOffset) {
148                Vector2f loc = Misc.getPointWithinRadius(prev.offset, maxOffset);
149                prev.offset.set(loc);
150        }
151        
152        public void advance(float amount) {
153                if (Global.getCombatEngine().isPaused()) return;
154                
155                List<ParticleData> remove = new ArrayList<ParticleData>();
156                for (ParticleData p : particles) {
157                        p.advance(amount);
158                        if (p.elapsed >= p.maxDur) {
159                                remove.add(p);
160                        }
161                }
162                particles.removeAll(remove);
163                
164                remove = new ArrayList<ParticleData>();
165                for (ParticleData p : darkParticles) {
166                        p.advance(amount);
167                        if (p.elapsed >= p.maxDur) {
168                                remove.add(p);
169                        }
170                }
171                darkParticles.removeAll(remove);
172        }
173
174
175        public boolean isExpired() {
176                return particles.isEmpty() && darkParticles.isEmpty();
177        }
178        
179        protected float getGlobalAlphaMult() {
180                return 1f;
181        }
182
183        protected Float baseFacing = null;
184        public void render(CombatEngineLayers layer, ViewportAPI viewport) {
185                render(layer, viewport, null);
186        }
187        
188        public void render(CombatEngineLayers layer, ViewportAPI viewport, DamagingProjectileAPI proj) {
189                float x = entity.getLocation().x;
190                float y = entity.getLocation().y;
191                
192                float b = viewport.getAlphaMult();
193                
194                if (proj != null && baseFacing == null) {
195                        baseFacing = proj.getFacing();
196                }
197                
198                if (layer == normalLayer) {
199                        for (ParticleData p : particles) {
200                                float size = p.baseSize * p.scale;
201                                
202                                Vector2f offset = p.offset;
203                                float diff = 0f;
204                                if (baseFacing != null && proj != null) {
205                                        diff = Misc.getAngleDiff(baseFacing, proj.getFacing());
206                                        if (Math.abs(diff) > 0.1f) {
207                                                offset = Misc.rotateAroundOrigin(offset, diff);
208                                        }
209                                }
210                                Vector2f loc = new Vector2f(x + offset.x, y + offset.y);
211                                
212                                float alphaMult = getGlobalAlphaMult();
213                                
214                                p.sprite.setAngle(p.angle);
215                                p.sprite.setSize(size, size);
216                                p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness());
217                                p.sprite.setColor(p.color);
218                                p.sprite.renderAtCenter(loc.x, loc.y);
219                        }
220                } else if (layer == darkLayer) {
221                        GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
222                        
223                        for (ParticleData p : darkParticles) {
224                                //float size = proj.getProjectileSpec().getWidth() * 0.6f;
225                                float size = p.baseSize * p.scale;
226                                
227                                Vector2f offset = p.offset;
228                                float diff = 0f;
229                                if (baseFacing != null && proj != null) {
230                                        diff = Misc.getAngleDiff(baseFacing, proj.getFacing());
231                                        if (Math.abs(diff) > 0.1f) {
232                                                offset = Misc.rotateAroundOrigin(offset, diff);
233                                        }
234                                }
235                                Vector2f loc = new Vector2f(x + offset.x, y + offset.y);
236                                
237                                float alphaMult = getGlobalAlphaMult();
238                                
239                                p.sprite.setAngle(p.angle);
240                                p.sprite.setSize(size, size);
241                                p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness());
242                                p.sprite.setColor(p.color);
243                                p.sprite.renderAtCenter(loc.x, loc.y);
244                        }
245                        
246                        GL14.glBlendEquation(GL14.GL_FUNC_ADD);
247                }
248        }
249
250}
251
252
253
254