001package com.fs.starfarer.api.impl.combat.dweller;
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.CombatEngineAPI;
009import com.fs.starfarer.api.combat.CombatEntityAPI;
010import com.fs.starfarer.api.combat.EmpArcEntityAPI;
011import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams;
012import com.fs.starfarer.api.combat.ShipAPI;
013import com.fs.starfarer.api.impl.combat.RiftLanceEffect;
014import com.fs.starfarer.api.impl.combat.threat.RoilingSwarmEffect;
015import com.fs.starfarer.api.util.IntervalUtil;
016import com.fs.starfarer.api.util.Misc;
017
018public class DwellerShroud extends RoilingSwarmEffect {
019
020        public static Color SHROUD_COLOR = new Color(100, 0, 25, 255);
021        public static Color SHROUD_GLOW_COLOR = new Color(150, 0, 30, 255);
022        public static Color SHROUD_OVERLOAD_FRINGE_COLOR = new Color(150, 0, 0, 255);
023        
024        public static interface ShroudNegativeParticleFilter {
025                boolean isParticleOk(DwellerShroud shroud, Vector2f loc);
026        }
027        
028        public static class DwellerShroudParams extends RoilingSwarmParams {
029                public float overloadGlowSizeMult = 1f;
030                public float overloadArcThickness = 40f;
031                public float overloadArcCoreThickness = 20f;
032                public Color overloadArcFringeColor = SHROUD_OVERLOAD_FRINGE_COLOR;
033                public float overloadArcGenRate = 0.25f;
034                public float overloadArcOffsetMult = 1f;
035                
036                public float negativeParticleGenRate = 1f;
037                public float negativeParticleSizeMult = 1f;
038                public float negativeParticleVelMult = 1f;
039                public float negativeParticleDurMult = 1f;
040                public float negativeParticleSpeedCapMult = 1.5f;
041                public float negativeParticleSpeedCap = 10000f;
042                public float negativeParticleAreaMult = 1f;
043                public float negativeParticleClearCenterAreaRadius = 0f;
044                public boolean negativeParticleHighContrastMode = false;
045                public int negativeParticleNumBase = 11;
046                public int negativeParticleNumOverloaded = 5;
047                public int negativeParticleAlphaIntOverride = -1;
048                public Color negativeParticleColorOverride = null;
049                public ShroudNegativeParticleFilter negativeParticleFilter = null;
050        }
051        
052        
053        public static DwellerShroud getShroudFor(CombatEntityAPI entity) {
054                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(entity);
055                if (swarm instanceof DwellerShroud) {
056                        return (DwellerShroud) swarm;
057                }
058                return null;
059        }
060        
061        public static DwellerShroudParams createBaselineParams(CombatEntityAPI attachedTo) {
062                if (!(attachedTo instanceof ShipAPI)) {
063                        return null;
064                }
065                
066                ShipAPI ship = (ShipAPI) attachedTo;
067                DwellerShroudParams params = new DwellerShroudParams();
068                float radius = 20f;
069                int numMembers = 50;
070
071                
072//              "fx_particles1":"graphics/fx/fx_clouds00.png",
073//              "fx_particles2":"graphics/fx/fx_clouds01.png",
074//              "nebula_particles":"graphics/fx/nebula_colorless.png",
075//              "nebula_particles2":"graphics/fx/cleaner_clouds00.png",
076//              "dust_particles":"graphics/fx/dust_clouds_colorless.png",
077                
078//              params.spriteKey = "dust_particles";
079//              params.spriteKey = "nebula_particles";
080//              params.spriteKey = "fx_particles1";
081                
082                params.spriteCat = "dweller";
083                params.spriteKey = "dweller_pieces";
084                
085                params.despawnSound = null; // no free-flying swarms, all are ships that have an explosion sound
086                
087                params.baseDur = 1f;
088                params.durRange = 2f;
089                params.memberRespawnRate = 100f;
090                
091                params.memberExchangeClass = null;
092                params.flockingClass = null;
093                params.maxSpeed = ship.getMaxSpeedWithoutBoost() + 
094                                        Math.max(ship.getMaxSpeedWithoutBoost() * 0.25f + 50f, 100f);
095                
096                params.baseSpriteSize = 256f;
097                params.baseSpriteSize = 128f * 1.5f * 0.67f;
098                params.maxTurnRate = 120f;
099                
100                numMembers = 100;
101                radius = 150f;
102
103//              radius = 100;
104//              numMembers = 40;
105                
106                params.flashCoreRadiusMult = 0f;
107                //params.flashRadius = 0f;
108                params.flashRadius = 300f;
109                params.flashRadius = 150f;
110                params.renderFlashOnSameLayer = true;
111                params.flashRateMult = 0.25f;
112                //params.flashFrequency = 10f;
113                //params.flashFrequency = 20f;
114                //params.flashFrequency = 40f;
115                params.flashFrequency = 17f;
116                params.numToFlash = 2;
117                //params.flashFrequency = 50f;
118                params.flashProbability = 1f;
119                
120                params.swarmLeadsByFractionOfVelocity = 0f;
121                
122                params.alphaMult = 1f;
123                params.alphaMultBase = 1f;
124                params.alphaMultFlash = 1f;
125                
126//              params.alphaMult = 0.25f;
127//              params.negativeParticleGenRate = 0f;
128                
129                //params.color = RiftCascadeEffect.EXPLOSION_UNDERCOLOR;
130                params.color = SHROUD_COLOR;
131                params.flashFringeColor = SHROUD_GLOW_COLOR;
132                
133//              params.color = new Color(121, 56, 171, 255);
134//              params.color = Misc.setBrightness(params.color, 200);
135//              //params.flashFringeColor = new Color(7, 163, 169, 255);
136//              params.flashFringeColor = params.color;
137//              params.flashFringeColor = Misc.setBrightness(params.flashFringeColor, 250);
138                
139                params.flashCoreColor = Misc.setBrightness(params.color, 255);
140                
141                
142                //params.despawnDist = params.maxOffset + 300f;
143                
144                params.maxOffset = radius;
145                params.initialMembers = numMembers;
146                params.baseMembersToMaintain = params.initialMembers;
147                
148                return params;
149        }
150        
151        
152        
153        
154        protected IntervalUtil interval = new IntervalUtil(0.075f, 0.125f);
155        protected IntervalUtil overloadInterval = new IntervalUtil(0.075f, 0.125f);
156        //protected IntervalUtil ventingInterval = new IntervalUtil(0.075f, 0.125f);
157        protected ShipAPI ship;
158        protected DwellerShroudParams shroudParams;
159        
160        
161        public DwellerShroud(CombatEntityAPI attachedTo) {
162                this(attachedTo, createBaselineParams(attachedTo));
163        }
164        public DwellerShroud(CombatEntityAPI attachedTo, DwellerShroudParams params) {
165                super(attachedTo, params);
166                this.shroudParams = params;
167                if (attachedTo instanceof ShipAPI) {
168                        ship = (ShipAPI) attachedTo;
169                }
170        }
171
172        
173        
174        @Override
175        public int getNumMembersToMaintain() {
176                return super.getNumMembersToMaintain();
177        }
178
179        @Override
180        public void advance(float amount) {
181                super.advance(amount);
182
183                boolean venting = ship.getFluxTracker().isVenting();
184                boolean overloaded = ship.getFluxTracker().isOverloaded() || venting;
185//              overloaded = true;
186//              overloaded = !ship.getShield().isOn();
187                
188                if (overloaded) {
189                        params.springStretchMult = 1f;
190                        params.flashProbability = 0.25f;
191                        params.despawnDist = ship.getCollisionRadius();
192                        //params.alphaMult = 0.1f;
193                } else {
194                        params.springStretchMult = 10f;
195                        params.flashProbability = 1f;
196                        params.despawnDist = 0f;
197                        //params.alphaMult = 1f;
198                }
199                
200                
201//              ventingInterval.advance(amount * 1f);
202//              if (ventingInterval.intervalElapsed() && ship != null && venting) {
203//                      
204//              }
205                
206                float empArcGenRate = shroudParams.overloadArcGenRate;
207                overloadInterval.advance(amount * empArcGenRate * 1f);
208                if (overloadInterval.intervalElapsed() && ship != null && overloaded && !isDespawning()) {
209                        EmpArcParams params = new EmpArcParams();
210                        params.segmentLengthMult = 4f;
211                        params.glowSizeMult = 5f + ship.getFluxLevel() * 2f;
212                        params.glowSizeMult *= shroudParams.overloadGlowSizeMult;
213                        //params.zigZagReductionFactor = 0f;
214                        //params.brightSpotFadeFraction = 0.33f;
215                        //params.brightSpotFullFraction = 0.5f;
216                        //params.movementDurMax = 0.2f;
217                        //params.flickerRateMult = overloadRate;
218                        params.flickerRateMult = 0.5f + (float) Math.random() * 0.5f;
219                        
220                        Color fringe = this.params.flashFringeColor;
221                        //fringe = Misc.setAlpha(fringe, 127);
222//                      fringe = Misc.scaleColor(fringe, 0.75f);
223                        fringe = shroudParams.overloadArcFringeColor;
224                        //fringe = new Color(240,255,0,255);
225                        Color core = Color.white;
226
227                        float thickness = shroudParams.overloadArcThickness;
228                        
229                        Vector2f loc = ship.getLocation();
230                        float r = this.params.maxOffset;
231                        r = r * 0.5f + r * 0.5f * (float) Math.random();
232                        //r *= 1.5f;
233                        r *= shroudParams.overloadArcOffsetMult;
234                        Vector2f from = Misc.getPointAtRadius(loc, r);
235                        float angle = Misc.getAngleInDegrees(from, loc);
236                        angle = angle + 90f * ((float) Math.random() - 0.5f);
237                        Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle);
238                        float dist = this.params.maxOffset;
239                        dist = dist * 0.5f + dist * 0.5f * (float) Math.random();
240                        dist *= 1.5f;
241                        dist *= shroudParams.overloadArcOffsetMult;
242                        dir.scale(dist);
243                        Vector2f to = Vector2f.add(from, dir, new Vector2f());
244                        
245//                      float minBright = 200f;
246//                      if (dist * params.brightSpotFullFraction < minBright) {
247//                              params.brightSpotFullFraction = minBright / Math.max(minBright, dist);
248//                      }
249                        
250                        CombatEngineAPI engine = Global.getCombatEngine();
251                        EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArcVisual(
252                                        from, ship, to, ship, thickness, fringe, core, params);
253                                        
254                        arc.setCoreWidthOverride(shroudParams.overloadArcCoreThickness);
255                        
256                        Global.getSoundPlayer().playSound("dweller_venting_or_overloaded", 1f, 1f, to, ship.getVelocity());
257                        //arc.setSingleFlickerMode(true);
258                        //arc.setSingleFlickerMode(false);
259                }
260                
261                //params.alphaMult = 1f;
262                //params.alphaMult = 0f;
263                interval.advance(amount * shroudParams.negativeParticleGenRate);
264                if (interval.intervalElapsed()) {
265                        CombatEngineAPI engine = Global.getCombatEngine();
266                        
267                        boolean smallerDark = false;
268                        //smallerDark = true;
269                        Color c = RiftLanceEffect.getColorForDarkening(params.color);
270                        c = Misc.setAlpha(c, 100);
271                        int num = shroudParams.negativeParticleNumBase;
272                        if (smallerDark) num = 8;
273                        if (overloaded) {
274                                c = Misc.setAlpha(c, 150);
275                                num = shroudParams.negativeParticleNumOverloaded;
276                                if (smallerDark) num = 4; 
277                        }
278                        if (shroudParams.negativeParticleHighContrastMode) {
279                                c = Misc.setAlpha(c, 150);
280                        }
281                        if (shroudParams.negativeParticleColorOverride != null) {
282                                c = shroudParams.negativeParticleColorOverride;
283                        }
284                        if (shroudParams.negativeParticleAlphaIntOverride >= 0) {
285                                c = Misc.setAlpha(c, shroudParams.negativeParticleAlphaIntOverride);
286                        }
287                        
288                        float baseDuration = 2f;
289                        Vector2f vel = new Vector2f(attachedTo.getVelocity());
290                        float speed = vel.length();
291                        if (attachedTo instanceof ShipAPI) {
292                                float maxSpeed = ((ShipAPI)attachedTo).getMaxSpeed() * shroudParams.negativeParticleSpeedCapMult;
293                                maxSpeed = Math.min(maxSpeed, shroudParams.negativeParticleSpeedCap);
294                                if (speed > maxSpeed && speed > 1f) {
295                                        vel.scale(maxSpeed / speed);
296                                }
297                        }
298                        
299                        float baseSize = params.maxOffset * 2f;
300                        //baseSize = params.maxOffset * 1f;
301                        
302                        //float size = ship.getCollisionRadius() * 0.35f;
303                        float size = baseSize * 0.33f;
304                        
305                        float extraDur = 0f;
306                        
307                        // so that switching the view to another ship near a dweller part
308                        // doesn't result in it not having negative particles
309                        Global.getCombatEngine().getViewport().setEverythingNearViewport(true);
310                        
311                        //for (int i = 0; i < 3; i++) {
312                        for (int i = 0; i < num; i++) {
313                        //for (int i = 0; i < 7; i++) {
314                                Vector2f point = new Vector2f(attachedTo.getLocation());
315                                float min = shroudParams.negativeParticleClearCenterAreaRadius;
316                                if (min > 0) {
317                                        point = Misc.getPointWithinRadiusUniform(point, min, 
318                                                        Math.max(min, baseSize * 0.75f * (smallerDark ? 0.85f : 1f) * shroudParams.negativeParticleAreaMult), Misc.random);
319                                } else {
320                                        point = Misc.getPointWithinRadiusUniform(point, 
321                                                        baseSize * 0.75f * (smallerDark ? 0.85f : 1f) * shroudParams.negativeParticleAreaMult, Misc.random);
322                                }
323                                
324                                float dur = baseDuration + baseDuration * (float) Math.random();
325                                dur += extraDur;
326                                float nSize = size;
327                                Vector2f pt = Misc.getPointWithinRadius(point, nSize * 0.5f);
328                                Vector2f v = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f);
329                                v.scale(nSize + nSize * (float) Math.random() * 0.5f);
330                                v.scale(0.2f * shroudParams.negativeParticleVelMult);
331                                Vector2f.add(vel, v, v);
332                                
333                                float maxSpeed = nSize * 1.5f * 0.2f; 
334                                float minSpeed = nSize * 1f * 0.2f; 
335                                float overMin = v.length() - minSpeed;
336                                if (overMin > 0) {
337                                        float durMult = 1f - overMin / (maxSpeed - minSpeed);
338                                        if (durMult < 0.1f) durMult = 0.1f;
339                                        dur *= 0.5f + 0.5f * durMult;
340                                }
341                                
342                                dur *= shroudParams.negativeParticleDurMult;
343                                
344                                //nSize *= 1.5f;
345                                
346                                if (shroudParams.negativeParticleFilter != null && 
347                                                !shroudParams.negativeParticleFilter.isParticleOk(this, pt)) {
348                                        continue;
349                                }
350                                
351                                engine.addNegativeNebulaParticle(pt, v, nSize * 1f * shroudParams.negativeParticleSizeMult, 2f,
352                                                                                                0.5f / dur, 0f, dur, c);
353                        }
354                        Global.getCombatEngine().getViewport().setEverythingNearViewport(false);
355                }
356                
357        }
358
359        public DwellerShroudParams getShroudParams() {
360                return shroudParams;
361        }
362}