001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.graphics.Color;
004import com.badlogic.gdx.math.Interpolation;
005import com.badlogic.gdx.scenes.scene2d.Action;
006import com.badlogic.gdx.scenes.scene2d.actions.Actions;
007import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction;
008import squidpony.ArrayTools;
009import squidpony.squidgrid.FOV;
010import squidpony.squidgrid.Radius;
011import squidpony.squidmath.Bresenham;
012import squidpony.squidmath.Coord;
013import squidpony.squidmath.DiverRNG;
014import squidpony.squidmath.GreasedRegion;
015import squidpony.squidmath.NumberTools;
016import squidpony.squidmath.SeededNoise;
017
018import java.util.Arrays;
019import java.util.List;
020
021/**
022 * Various special effects that can be applied to a {@link SquidPanel} or {@link SparseLayers} as an
023 * {@link com.badlogic.gdx.scenes.scene2d.Action}. The PanelEffect class is abstract and has implementations as static
024 * inner classes, such as {@link ExplosionEffect}. Each PanelEffect specifically affects one {@link IPackedColorPanel}
025 * (an interface that both SquidPanel and SparseLayers implement), which can be a layer in a SquidLayers object or a
026 * SquidPanel/SparseLayers on its own. By adding the PanelEffect to any actor using
027 * {@link com.badlogic.gdx.scenes.scene2d.Actor#addAction(Action)} when the Actor has its act() method called after the
028 * normal map parts of the panel have been placed, the PanelEffect will advance and change what chars/colors are in
029 * the panel as specified in its implementation of the {@link #update(float)} method. Typically the Actor you add this
030 * to is the SquidPanel or SparseLayers this affects, but it could also be a SquidLayers. Most PanelEffect
031 * implementations should allow most configuration to be set in their constructors.
032 * <br>
033 * Created by Tommy Ettinger on 5/24/2017.
034 */
035public abstract class PanelEffect extends TemporalAction{
036    public IPackedColorPanel target;
037    public GreasedRegion validCells;
038
039    protected PanelEffect(IPackedColorPanel targeting)
040    {
041        target = targeting;
042        validCells = new GreasedRegion(targeting.gridWidth(), targeting.gridHeight()).allOn();
043    }
044    protected PanelEffect(IPackedColorPanel targeting, float duration)
045    {
046        target = targeting;
047        setDuration(duration);
048        validCells = new GreasedRegion(targeting.gridWidth(), targeting.gridHeight()).allOn();
049    }
050    protected PanelEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid)
051    {
052        target = targeting;
053        setDuration(duration);
054        validCells = valid;
055    }
056    public static class ExplosionEffect extends PanelEffect
057    {
058        /**
059         * Normally you should set this in the constructor, and not change it later.
060         */
061        public Coord center;
062        /**
063         * Normally you should set this in the constructor, and not change it later.
064         */
065        public int radius = 2;
066        /**
067         * The default explosion colors are normal for (non-chemical, non-electrical) fire and smoke, going from orange
068         * at the start to yellow, very light yellow, and then back to a different orange before going to smoke and
069         * fading out to translucent and then transparent by the end.
070         * <br>
071         * If you want to change the colors the explosion uses, you can either pass a List of Color (or SColor or other
072         * subclasses) to the constructor or change this array directly. The float items assigned to this should be the
073         * result of calling {@link Color#toFloatBits()} or possibly the result of mixing multiple existing floats with
074         * {@link SColor#lerpFloatColors(float, float, float)}; other floats that can be directly used by libGDX, that
075         * is, packed as ABGR floats (usually the docs will call this a packed float), can also be used.
076         */
077        public float[] colors = {
078                SColor.floatGet(0xFF4F00FF), // SColor.INTERNATIONAL_ORANGE
079                SColor.floatGet(0xFFB94EFF), // SColor.FLORAL_LEAF
080                SColor.floatGet(0xFDE910FF), // SColor.LEMON
081                SColor.floatGet(0xFFFACDFF), // SColor.LEMON_CHIFFON
082                SColor.floatGet(0xFF6600EE), // SColor.SAFETY_ORANGE
083                SColor.floatGet(0x595652DD), // SColor.DB_SOOT
084                SColor.floatGet(0x59565299)  // SColor.DB_SOOT
085        };
086        /**
087         * The internal representation of how affected each cell is by the explosion, based on proximity to center.
088         */
089        public double[][] lightMap;
090        /**
091         * The raw list of Coords that might be affected by the explosion; may include some cells that aren't going to
092         * show as exploding (it usually has some false positives), but shouldn't exclude any cells that should show as
093         * such (no false negatives). You can edit this if you need to, but it isn't recommended.
094         */
095        public List<Coord> affected;
096        /**
097         * Constructs an ExplosionEffect with explicit settings for some fields. The valid cells this can affect will be
098         * the full expanse of the IPackedColorPanel. The duration will be 1 second.
099         * @param targeting the IPackedColorPanel to affect
100         * @param center the center of the explosion
101         * @param radius the radius of the explosion, in cells
102         */
103
104        public ExplosionEffect(IPackedColorPanel targeting, Coord center, int radius)
105        {
106            this(targeting, 1f, center, radius);
107        }
108        /**
109         * Constructs an ExplosionEffect with explicit settings for some fields. The valid cells this can affect will be
110         * the full expanse of the IPackedColorPanel.
111         * @param targeting the IPackedColorPanel to affect
112         * @param duration the duration of this PanelEffect in seconds, as a float
113         * @param center the center of the explosion
114         * @param radius the radius of the explosion, in cells
115         */
116        public ExplosionEffect(IPackedColorPanel targeting, float duration, Coord center, int radius)
117        {
118            super(targeting, duration);
119            this.center = center;
120            this.radius = radius;
121            double[][] resMap = new double[validCells.width][validCells.height];
122            lightMap = new double[validCells.width][validCells.height];
123            FOV.reuseFOV(resMap, lightMap, center.x, center.y, radius + 0.5);
124            affected = Radius.inCircle(center.x, center.y, radius, false, validCells.width, validCells.height);
125        }
126        /**
127         * Constructs an ExplosionEffect with explicit settings for most fields.
128         * @param targeting the IPackedColorPanel to affect
129         * @param duration the duration of this PanelEffect in seconds, as a float
130         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
131         * @param center the center of the explosion
132         * @param radius the radius of the explosion, in cells
133         */
134        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius)
135        {
136            super(targeting, duration, valid);
137            this.center = center;
138            this.radius = radius;
139            double[][] resMap = ArrayTools.fill(1.0, validCells.width, validCells.height);
140            validCells.writeDoublesInto(resMap, 0.0);
141            lightMap = new double[validCells.width][validCells.height];
142            FOV.reuseFOV(resMap, lightMap, center.x, center.y, radius + 0.5);
143            validCells.not().writeDoublesInto(lightMap, 0.0);
144            validCells.not();
145            affected = Radius.inCircle(center.x, center.y, radius, false, validCells.width, validCells.height);
146        }
147
148        /**
149         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
150         * objects that it will use to color the explosion instead of using fiery/smoke colors.
151         * @param targeting the IPackedColorPanel to affect
152         * @param duration the duration of this PanelEffect in seconds, as a float
153         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
154         * @param center the center of the explosion
155         * @param radius the radius of the explosion, in cells
156         * @param coloring a List of Color or subclasses thereof that will replace the default fire/smoke colors here
157         */
158        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, List<? extends Color> coloring)
159        {
160            this(targeting, duration, valid, center, radius);
161            if(colors.length != coloring.size())
162                colors = new float[coloring.size()];
163            for (int i = 0; i < colors.length; i++) {
164                colors[i] = coloring.get(i).toFloatBits();
165            }
166        }
167
168        /**
169         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
170         * objects that it will use to color the explosion instead of using fiery/smoke colors.
171         * @param targeting the IPackedColorPanel to affect
172         * @param duration the duration of this PanelEffect in seconds, as a float
173         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
174         * @param center the center of the explosion
175         * @param radius the radius of the explosion, in cells
176         * @param coloring an array of colors as packed floats that will replace the default fire/smoke colors here
177         */
178        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, float[] coloring)
179        {
180            this(targeting, duration, valid, center, radius);
181            if(coloring == null) return;
182            if(colors.length != coloring.length)
183                colors = new float[coloring.length];
184            System.arraycopy(coloring, 0, colors, 0, coloring.length);
185        }
186        /**
187         * Constructs an ExplosionEffect with explicit settings for most fields; this constructor allows the case where
188         * an explosion is directed in a cone or sector shape. It will center the sector on {@code angle} (in degrees)
189         * and will cover an amount of the circular area (in degrees) equal to {@code span}.
190         * @param targeting the IPackedColorPanel to affect
191         * @param duration the duration of this PanelEffect in seconds, as a float
192         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
193         * @param center the center of the explosion
194         * @param radius the radius of the explosion, in cells
195         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
196         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
197         */
198        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span)
199        {
200            super(targeting, duration, valid);
201            this.center = center;
202            this.radius = radius;
203            double[][] resMap = ArrayTools.fill(1.0, validCells.width, validCells.height);
204            validCells.writeDoublesInto(resMap, 0.0);
205            lightMap = new double[validCells.width][validCells.height];
206            FOV.reuseFOV(resMap, lightMap, center.x, center.y, radius + 0.5, Radius.CIRCLE, angle, span);
207            validCells.not().writeDoublesInto(lightMap, 0.0);
208            validCells.not();
209            affected = Radius.inCircle(center.x, center.y, radius, false, validCells.width, validCells.height);
210        }
211
212        /**
213         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
214         * objects that it will use to color the explosion instead of using fiery/smoke colors; this constructor allows
215         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
216         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
217         * @param targeting the IPackedColorPanel to affect
218         * @param duration the duration of this PanelEffect in seconds, as a float
219         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
220         * @param center the center of the explosion
221         * @param radius the radius of the explosion, in cells
222         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
223         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
224         * @param coloring a List of Color or subclasses thereof that will replace the default fire/smoke colors here
225         */
226        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, List<? extends Color> coloring)
227        {
228            this(targeting, duration, valid, center, radius, angle, span);
229            if(colors.length != coloring.size())
230                colors = new float[coloring.size()];
231            for (int i = 0; i < colors.length; i++) {
232                colors[i] = coloring.get(i).toFloatBits();
233            }
234        }
235
236        /**
237         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
238         * objects that it will use to color the explosion instead of using fiery/smoke colors; this constructor allows
239         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
240         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
241         * @param targeting the IPackedColorPanel to affect
242         * @param duration the duration of this PanelEffect in seconds, as a float
243         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
244         * @param center the center of the explosion
245         * @param radius the radius of the explosion, in cells
246         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
247         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
248         * @param coloring an array of colors as packed floats that will replace the default fire/smoke colors here
249         */
250        public ExplosionEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, float[] coloring)
251        {
252            this(targeting, duration, valid, center, radius, angle, span);
253            if(coloring == null) return;
254            if(colors.length != coloring.length)
255                colors = new float[coloring.length];
256            System.arraycopy(coloring, 0, colors, 0, coloring.length);
257        }
258        /**
259         * Called each frame.
260         *
261         * @param percent The percentage of completion for this action, growing from 0 to 1 over the duration. If
262         *                {@link #setReverse(boolean) reversed}, this will shrink from 1 to 0.
263         */
264        @Override
265        protected void update(float percent) {
266            int len = affected.size();
267            Coord c;
268            float f, color, light;
269            int idx, seed = System.identityHashCode(this);
270            for (int i = 0; i < len; i++) {
271                c = affected.get(i);
272                if((light = (float) lightMap[c.x][c.y]) <= 0.0)// || 0.6 * (lightMap[c.x][c.y] + percent) < 0.25)
273                    continue;
274                f = (float)SeededNoise.noise(c.x * 1.5, c.y * 1.5, percent * 5, seed)
275                        * 0.17f + percent * 1.2f;
276                if(f < 0f || 0.5 * light + f < 0.4)
277                    continue;
278                idx = (int) (f * colors.length);
279                if(idx >= colors.length - 1)
280                    color = SColor.lerpFloatColors(colors[colors.length-1], NumberTools.setSelectedByte(colors[colors.length-1], 3, (byte)0), (Math.min(0.99f, f) * colors.length) % 1f);
281                else
282                    color = SColor.lerpFloatColors(colors[idx], colors[idx+1], (f * colors.length) % 1f);
283                target.blend(c.x, c.y, color, SColor.alphaOfFloatF(color) * light * 0.25f + 0.75f);
284            }
285        }
286        /**
287         * Sets the colors this ExplosionEffect uses to go from through various shades of gray-purple before fading.
288         * Meant for electrical bursts, this will affect character foregrounds in a GibberishEffect. This should look
289         * like sparks in GibberishEffect if the chars in {@link GibberishEffect#choices} are selected in a way that
290         * fits that theme.
291         */
292        public ExplosionEffect useElectricColors()
293        {
294            colors[0] = SColor.floatGet(0xCCCCFFEE); // SColor.PERIWINKLE
295            colors[1] = SColor.floatGet(0xBF00FFFF); // SColor.ELECTRIC_PURPLE
296            colors[2] = SColor.floatGet(0xCC99CCFF); // SColor.MEDIUM_LAVENDER_MAGENTA
297            colors[3] = SColor.floatGet(0xC8A2C8EE); // SColor.LILAC
298            colors[4] = SColor.floatGet(0xBF00FFDD); // SColor.ELECTRIC_PURPLE
299            colors[5] = SColor.floatGet(0x6022EEBB); // SColor.ELECTRIC_INDIGO
300            colors[6] = SColor.floatGet(0x4B008277); // SColor.INDIGO
301            return this;
302        }
303
304        /**
305         * Sets the colors this ExplosionEffect uses to go from orange, to yellow, to orange, to dark gray, then fade.
306         * Meant for fiery explosions with smoke, this will affect character foregrounds in a GibberishEffect.
307         * This may look more like a fiery blast if used with an ExplosionEffect than a GibberishEffect.
308         */
309        public ExplosionEffect useFieryColors()
310        {
311            colors[0] = SColor.floatGet(0xFF4F00FF); // SColor.INTERNATIONAL_ORANGE
312            colors[1] = SColor.floatGet(0xFFB94EFF); // SColor.FLORAL_LEAF
313            colors[2] = SColor.floatGet(0xFDE910FF); // SColor.LEMON
314            colors[3] = SColor.floatGet(0xFFFACDFF); // SColor.LEMON_CHIFFON
315            colors[4] = SColor.floatGet(0xFF6600EE); // SColor.SAFETY_ORANGE
316            colors[5] = SColor.floatGet(0x595652DD); // SColor.DB_SOOT
317            colors[6] = SColor.floatGet(0x59565299); // SColor.DB_SOOT
318            return this;
319        }
320
321    }
322    public static class GibberishEffect extends ExplosionEffect
323    {
324        /**
325         * This char array contains all characters that can be used in the foreground of this effect. You can assign
326         * another char array, such as if you take {@link squidpony.StringKit#PUNCTUATION} and call
327         * {@link String#toCharArray()} on it, to this at any time between calls to {@link #update(float)} (which is
328         * usually called indirectly via Stage's {@link com.badlogic.gdx.scenes.scene2d.Stage#act()} method if this has
329         * been added to an Actor on that Stage). These chars are pseudo-randomly selected approximately once every
330         * eighth of a second, and may change sooner if the effect expands more quickly than that.
331         */
332        public char[] choices = "`~!@#$%^&*()-_=+\\|][}{'\";:/?.>,<".toCharArray();
333
334        public GibberishEffect(IPackedColorPanel targeting, Coord center, int radius)
335        {
336            super(targeting, 1f, center, radius);
337            useElectricColors();
338        }
339        /**
340         * Constructs an ExplosionEffect with explicit settings for some fields. The valid cells this can affect will be
341         * the full expanse of the IPackedColorPanel.
342         * @param targeting the IPackedColorPanel to affect
343         * @param duration the duration of this PanelEffect in seconds, as a float
344         * @param center the center of the explosion
345         * @param radius the radius of the explosion, in cells
346         */
347        public GibberishEffect(IPackedColorPanel targeting, float duration, Coord center, int radius) {
348            super(targeting, duration, center, radius);
349            useElectricColors();
350        }
351        /**
352         * Constructs an ExplosionEffect with explicit settings for some fields. The valid cells this can affect will be
353         * the full expanse of the IPackedColorPanel.
354         * @param targeting the IPackedColorPanel to affect
355         * @param duration the duration of this PanelEffect in seconds, as a float
356         * @param center the center of the explosion
357         * @param radius the radius of the explosion, in cells
358         */
359        public GibberishEffect(IPackedColorPanel targeting, float duration, Coord center, int radius, char[] choices) {
360            super(targeting, duration, center, radius);
361            this.choices = choices;
362            useElectricColors();
363        }
364        /**
365         * Constructs an ExplosionEffect with explicit settings for most fields.
366         * @param targeting the IPackedColorPanel to affect
367         * @param duration the duration of this PanelEffect in seconds, as a float
368         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
369         * @param center the center of the explosion
370         * @param radius the radius of the explosion, in cells
371         */
372        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius)
373        {
374            super(targeting, duration, valid, center, radius);
375            useElectricColors();
376        }
377        /**
378         * Constructs an ExplosionEffect with explicit settings for most fields.
379         * @param targeting the IPackedColorPanel to affect
380         * @param duration the duration of this PanelEffect in seconds, as a float
381         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
382         * @param center the center of the explosion
383         * @param radius the radius of the explosion, in cells
384         */
385        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, char[] choices)
386        {
387            super(targeting, duration, valid, center, radius);
388            this.choices = choices;
389            useElectricColors();
390        }
391
392        /**
393         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
394         * objects that it will use to color the explosion instead of using purple spark colors.
395         * @param targeting the IPackedColorPanel to affect
396         * @param duration the duration of this PanelEffect in seconds, as a float
397         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
398         * @param center the center of the explosion
399         * @param radius the radius of the explosion, in cells
400         * @param coloring a List of Color or subclasses thereof that will replace the default purple spark colors here
401         */
402        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, List<? extends Color> coloring)
403        {
404            super(targeting, duration, valid, center, radius, coloring);
405        }
406        /**
407         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
408         * objects that it will use to color the explosion instead of using purple spark colors.
409         * @param targeting the IPackedColorPanel to affect
410         * @param duration the duration of this PanelEffect in seconds, as a float
411         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
412         * @param center the center of the explosion
413         * @param radius the radius of the explosion, in cells
414         * @param coloring a List of Color or subclasses thereof that will replace the default purple spark colors here
415         */
416        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, List<? extends Color> coloring, char[] choices)
417        {
418            super(targeting, duration, valid, center, radius, coloring);
419            this.choices = choices;
420        }
421
422        /**
423         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
424         * objects that it will use to color the explosion instead of using purple spark colors.
425         * @param targeting the IPackedColorPanel to affect
426         * @param duration the duration of this PanelEffect in seconds, as a float
427         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
428         * @param center the center of the explosion
429         * @param radius the radius of the explosion, in cells
430         * @param coloring an array of colors as packed floats that will replace the default purple spark colors here
431         */
432        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, float[] coloring)
433        {
434            super(targeting, duration, valid, center, radius, coloring);
435        }
436        /**
437         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
438         * objects that it will use to color the explosion instead of using purple spark colors.
439         * @param targeting the IPackedColorPanel to affect
440         * @param duration the duration of this PanelEffect in seconds, as a float
441         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
442         * @param center the center of the explosion
443         * @param radius the radius of the explosion, in cells
444         * @param coloring an array of colors as packed floats that will replace the default purple spark colors here
445         */
446        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, float[] coloring, char[] choices)
447        {
448            super(targeting, duration, valid, center, radius, coloring);
449            this.choices = choices;
450        }
451        /**
452         * Constructs an ExplosionEffect with explicit settings for most fields; this constructor allows
453         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
454         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
455         * @param targeting the IPackedColorPanel to affect
456         * @param duration the duration of this PanelEffect in seconds, as a float
457         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
458         * @param center the center of the explosion
459         * @param radius the radius of the explosion, in cells
460         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
461         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
462         */
463        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, char[] choices)
464        {
465            super(targeting, duration, valid, center, radius, angle, span);
466            this.choices = choices;
467            useElectricColors();
468        }
469
470        /**
471         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
472         * objects that it will use to color the explosion instead of using purple spark colors; this constructor allows
473         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
474         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
475         * @param targeting the IPackedColorPanel to affect
476         * @param duration the duration of this PanelEffect in seconds, as a float
477         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
478         * @param center the center of the explosion
479         * @param radius the radius of the explosion, in cells
480         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
481         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
482         * @param coloring a List of Color or subclasses thereof that will replace the default purple spark colors here
483         */
484        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, List<? extends Color> coloring)
485        {
486            super(targeting, duration, valid, center, radius, angle, span, coloring);
487        }
488        /**
489         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
490         * objects that it will use to color the explosion instead of using purple spark colors; this constructor allows
491         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
492         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
493         * @param targeting the IPackedColorPanel to affect
494         * @param duration the duration of this PanelEffect in seconds, as a float
495         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
496         * @param center the center of the explosion
497         * @param radius the radius of the explosion, in cells
498         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
499         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
500         * @param coloring a List of Color or subclasses thereof that will replace the default purple spark colors here
501         */
502        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, List<? extends Color> coloring, char[] choices)
503        {
504            super(targeting, duration, valid, center, radius, angle, span, coloring);
505            this.choices = choices;
506        }
507
508        /**
509         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
510         * objects that it will use to color the explosion instead of using purple spark colors; this constructor allows
511         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
512         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
513         * @param targeting the IPackedColorPanel to affect
514         * @param duration the duration of this PanelEffect in seconds, as a float
515         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
516         * @param center the center of the explosion
517         * @param radius the radius of the explosion, in cells
518         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
519         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
520         * @param coloring an array of colors as packed floats that will replace the default purple spark colors here
521         */
522        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span,  float[] coloring)
523        {
524            super(targeting, duration, valid, center, radius, angle, span, coloring);
525        }
526        /**
527         * Constructs an ExplosionEffect with explicit settings for most fields but also an alternate group of Color
528         * objects that it will use to color the explosion instead of using purple spark colors; this constructor allows
529         * the case where an explosion is directed in a cone or sector shape. It will center the sector on {@code angle}
530         * (in degrees) and will cover an amount of the circular area (in degrees) equal to {@code span}.
531         * @param targeting the IPackedColorPanel to affect
532         * @param duration the duration of this PanelEffect in seconds, as a float
533         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
534         * @param center the center of the explosion
535         * @param radius the radius of the explosion, in cells
536         * @param angle the angle, in degrees, that will be the center of the sector-shaped effect
537         * @param span the span, in degrees, of the full arc at the end of the sector-shaped effect
538         * @param coloring an array of colors as packed floats that will replace the default purple spark colors here
539         */
540        public GibberishEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, float[] coloring, char[] choices)
541        {
542            super(targeting, duration, valid, center, radius, angle, span, coloring);
543            this.choices = choices;
544        }
545        /**
546         * Called each frame.
547         *
548         * @param percent The percentage of completion for this action, growing from 0 to 1 over the duration. If
549         *                {@link #setReverse(boolean) reversed}, this will shrink from 1 to 0.
550         */
551        @Override
552        protected void update(float percent) {
553            int len = affected.size();
554            Coord c;
555            float f, color;
556            int idx, seed = System.identityHashCode(this), clen = choices.length;
557            final long tick = DiverRNG.determine((System.currentTimeMillis() >>> 7) * seed);
558            for (int i = 0; i < len; i++) {
559                c = affected.get(i);
560                if(lightMap[c.x][c.y] <= 0.0)// || 0.6 * (lightMap[c.x][c.y] + percent) < 0.25)
561                    continue;
562                f = (float)SeededNoise.noise(c.x * 1.5, c.y * 1.5, percent * 5, seed)
563                        * 0.17f + percent * 1.2f;
564                if(f < 0f || 0.5 * lightMap[c.x][c.y] + f < 0.4)
565                    continue;
566                idx = (int) (f * colors.length);
567                if(idx >= colors.length - 1)
568                    color = SColor.lerpFloatColors(colors[colors.length-1], NumberTools.setSelectedByte(colors[colors.length-1], 3, (byte)0), (Math.min(0.99f, f) * colors.length) % 1f);
569                else
570                    color = SColor.lerpFloatColors(colors[idx], colors[idx+1], (f * colors.length) % 1f);
571                target.put(c.x, c.y, choices[DiverRNG.determineBounded(tick + i, clen)], color);
572            }
573        }
574
575    }
576    public static class PulseEffect extends ExplosionEffect
577    {
578        public PulseEffect(IPackedColorPanel targeting, Coord center, int radius) {
579            super(targeting, center, radius);
580        }
581
582        public PulseEffect(IPackedColorPanel targeting, float duration, Coord center, int radius) {
583            super(targeting, duration, center, radius);
584        }
585
586        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius) {
587            super(targeting, duration, valid, center, radius);
588        }
589
590        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, List<? extends Color> coloring) {
591            super(targeting, duration, valid, center, radius, coloring);
592        }
593
594        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, float[] coloring) {
595            super(targeting, duration, valid, center, radius, coloring);
596        }
597
598        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span) {
599            super(targeting, duration, valid, center, radius, angle, span);
600        }
601
602        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, List<? extends Color> coloring) {
603            super(targeting, duration, valid, center, radius, angle, span, coloring);
604        }
605
606        public PulseEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord center, int radius, double angle, double span, float[] coloring) {
607            super(targeting, duration, valid, center, radius, angle, span, coloring);
608        }
609        @Override
610        protected void update(float percent) {
611            int len = affected.size();
612            Coord c;
613            float f, light;
614            int seed = System.identityHashCode(this);
615            for (int i = 0; i < len; i++) {
616                c = affected.get(i);
617                if((light = (float) lightMap[c.x][c.y]) <= 0f)// || 0.6 * (lightMap[c.x][c.y] + percent) < 0.25)
618                    continue;
619                f = (float)SeededNoise.noise(c.x * 0.3, c.y * 0.3, percent * 1.3, seed)
620                        * 0.498f + 0.4999f;
621                target.blend(c.x, c.y,
622                        SColor.lerpFloatColors(colors[(int) (f * colors.length)],
623                                colors[((int) (f * colors.length) + 1) % colors.length],
624                                (f * colors.length) % 1f), NumberTools.swayTight(percent * 2f) * light);
625            }
626        }
627    }
628    public static class ProjectileEffect extends PanelEffect
629    {
630        /**
631         * Normally you should set this in the constructor, and not change it later.
632         */
633        public Coord startPoint;
634        /**
635         * Normally you should set this in the constructor, and not change it later.
636         */
637        public Coord endPoint;
638
639        /**
640         * The char to show at each stage of the projectile's path; defaults to a Unicode bullet symbol, '·'.
641         */
642        public char shown = '·';
643        /**
644         * The color used for the projectile as a packed float; defaults to white.
645         */
646        public float color = SColor.FLOAT_WHITE;
647        /**
648         * The raw list of Coords that might be affected by the projectile, or are on its (potential) path. You can edit
649         * this if you need to, but it isn't recommended; because it is an array you would need to assign a new Coord
650         * array if the length changes.
651         */
652        public Coord[] affected;
653        /**
654         * Constructs a ProjectileEffect with explicit settings for some fields. The valid cells this can affect will be
655         * the full expanse of the IPackedColorPanel. The duration will be 1 second.
656         * @param targeting the IPackedColorPanel to affect
657         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
658         * @param endPoint the point to try to hit with the projectile; this should always succeed with no obstructions
659         */
660        public ProjectileEffect(IPackedColorPanel targeting, Coord startPoint, Coord endPoint)
661        {
662            this(targeting, 1f, startPoint, endPoint);
663        }
664        /**
665         * Constructs a ProjectileEffect with explicit settings for some fields. The valid cells this can affect will be
666         * the full expanse of the IPackedColorPanel.
667         * @param targeting the IPackedColorPanel to affect
668         * @param duration the duration of this PanelEffect in seconds, as a float
669         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
670         * @param endPoint the point to try to hit with the projectile; this should always succeed with no obstructions
671         */
672        public ProjectileEffect(IPackedColorPanel targeting, float duration, Coord startPoint, Coord endPoint)
673        {
674            super(targeting, duration);
675            this.startPoint = startPoint;
676            this.endPoint = endPoint;
677            affected = Bresenham.line2D_(startPoint.x,  startPoint.y, endPoint.x, endPoint.y);
678        }
679        /**
680         * Constructs a ProjectileEffect with explicit settings for most fields.
681         * @param targeting the IPackedColorPanel to affect
682         * @param duration the duration of this PanelEffect in seconds, as a float
683         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
684         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
685         * @param endPoint the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
686         */
687        public ProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint)
688        {
689            super(targeting, duration, valid);
690            this.startPoint = startPoint;
691            this.endPoint = endPoint;
692            affected = Bresenham.line2D_(startPoint.x,  startPoint.y, endPoint.x, endPoint.y);
693            for (int i = 0; i < affected.length; i++) {
694                if(!validCells.contains(affected[i]))
695                    affected[i] = null;
696            }
697        }
698
699        /**
700         * Constructs a ProjectileEffect with explicit settings for most fields but also an alternate Color
701         * object for the projectile instead of the default white color.
702         * @param targeting the IPackedColorPanel to affect
703         * @param duration the duration of this PanelEffect in seconds, as a float
704         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
705         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
706         * @param endPoint the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
707         * @param shown the char to show at each step of the projectile's path as it advances
708         * @param coloring a Color or subclass thereof that will replace the default white color here
709         */
710        public ProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint, char shown, Color coloring)
711        {
712            this(targeting, duration, valid, startPoint, endPoint);
713            this.shown = shown;
714            if(coloring != null)
715                color = coloring.toFloatBits();
716        }
717
718        /**
719         * Constructs a ProjectileEffect with explicit settings for most fields but also an alternate Color
720         * object for the projectile instead of the default white color.
721         * @param targeting the IPackedColorPanel to affect
722         * @param duration the duration of this PanelEffect in seconds, as a float
723         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
724         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
725         * @param endPoint the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
726         * @param shown the char to show at each step of the projectile's path as it advances
727         * @param coloring an array of colors as packed floats that will replace the default white color here
728         */
729        public ProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint, char shown, float coloring)
730        {
731            this(targeting, duration, valid, startPoint, endPoint);
732            this.shown = shown;
733            color = coloring;
734        }
735
736        /**
737         * Makes this ProjectileEffect take an "arc-like" path toward the target, where it is fast at the
738         * beginning and end of its motion and is reaching the height of its arc at the center.
739         */
740        public void useArcPathInterpolation()
741        {
742            setInterpolation(fastInSlowMidFastOut);
743        }
744
745        /**
746         * Makes this ProjectileEffect take a direct path to the target, traveling at uniform speed throughout its path.
747         */
748        public void useStraightPathInterpolation()
749        {
750            setInterpolation(Interpolation.linear);
751        }
752        /**
753         * Called each frame.
754         *
755         * @param percent The percentage of completion for this action, growing from 0 to 1 over the duration. If
756         *                {@link #setReverse(boolean) reversed}, this will shrink from 1 to 0.
757         */
758        @Override
759        protected void update(float percent) {
760            int len = affected.length, index = (int)((len - 1) * percent);
761            if(index < 0 || index >= len) return;
762            Coord c = affected[index];
763            if(c != null)
764            {
765                target.put(c.x, c.y, shown, color);
766            }
767            else {
768                for (int i = index + 1; i < len; i++) {
769                    affected[i] = null;
770                }
771            }
772        }
773    }
774
775    /**
776     * Almost exactly lke {@link ProjectileEffect}, but its duration specifies the amount of time to spend crossing each
777     * cell (in seconds), not the duration of the entire effect. For ranged weapons like arrows, a fixed duration for
778     * the effect (as in ProjectileEffect) would mean an arrow shot at a close-by target travels slowly and an arrow
779     * shot at a far-away target travels very quickly; this class avoids that issue. Note that the time spent by the
780     * whole effect will vary based on the Chebyshev distance between the start and end points. The speed the projectile
781     * travels at is also dependent on the size and aspect ratio of cells it travels over.
782     */
783    public static class SteadyProjectileEffect extends ProjectileEffect
784    {
785
786        /**
787         * Constructs a SteadyProjectileEffect with explicit settings for some fields. The valid cells this can affect will be
788         * the full expanse of the IPackedColorPanel. The duration will be 0.05 seconds per cell crossed.
789         *
790         * @param targeting  the IPackedColorPanel to affect
791         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
792         * @param endPoint   the point to try to hit with the projectile; this should always succeed with no obstructions
793         */
794        public SteadyProjectileEffect(IPackedColorPanel targeting, Coord startPoint, Coord endPoint) {
795            this(targeting, 0.05f, startPoint, endPoint);
796        }
797
798        /**
799         * Constructs a SteadyProjectileEffect with explicit settings for some fields. The valid cells this can affect will be
800         * the full expanse of the IPackedColorPanel.
801         *
802         * @param targeting  the IPackedColorPanel to affect
803         * @param duration   the time the projectile will take to cross one cell, in seconds as a float
804         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
805         * @param endPoint   the point to try to hit with the projectile; this should always succeed with no obstructions
806         */
807        public SteadyProjectileEffect(IPackedColorPanel targeting, float duration, Coord startPoint, Coord endPoint) {
808            super(targeting, (float) Radius.SQUARE.radius(startPoint, endPoint) * duration, startPoint, endPoint);
809        }
810
811        /**
812         * Constructs a SteadyProjectileEffect with explicit settings for most fields.
813         *
814         * @param targeting  the IPackedColorPanel to affect
815         * @param duration   the duration of this PanelEffect in seconds, as a float
816         * @param valid      the valid cells that can be changed by this PanelEffect, as a GreasedRegion
817         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
818         * @param endPoint   the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
819         */
820        public SteadyProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint) {
821            super(targeting, (float) Radius.SQUARE.radius(startPoint, endPoint) * duration, valid, startPoint, endPoint);
822        }
823
824        /**
825         * Constructs a SteadyProjectileEffect with explicit settings for most fields but also an alternate Color
826         * object for the projectile instead of the default white color.
827         *
828         * @param targeting  the IPackedColorPanel to affect
829         * @param duration   the time the projectile will take to cross one cell, in seconds as a float
830         * @param valid      the valid cells that can be changed by this PanelEffect, as a GreasedRegion
831         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
832         * @param endPoint   the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
833         * @param shown      the char to show at each step of the projectile's path as it advances
834         * @param coloring   a Color or subclass thereof that will replace the default white color here
835         */
836        public SteadyProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint, char shown, Color coloring) {
837            super(targeting, (float) Radius.SQUARE.radius(startPoint, endPoint) * duration, valid, startPoint, endPoint, shown, coloring);
838        }
839
840        /**
841         * Constructs a SteadyProjectileEffect with explicit settings for most fields but also an alternate Color
842         * object for the projectile instead of the default white color.
843         *
844         * @param targeting  the IPackedColorPanel to affect
845         * @param duration   the time the projectile will take to cross one cell, in seconds as a float
846         * @param valid      the valid cells that can be changed by this PanelEffect, as a GreasedRegion
847         * @param startPoint the starting point of the projectile; may be best if it is adjacent to whatever fires it
848         * @param endPoint   the point to try to hit with the projectile; this may not be reached if the path crosses a cell not in valid
849         * @param shown      the char to show at each step of the projectile's path as it advances
850         * @param coloring   an array of colors as packed floats that will replace the default white color here
851         */
852        public SteadyProjectileEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord startPoint, Coord endPoint, char shown, float coloring) {
853            super(targeting, (float) Radius.SQUARE.radius(startPoint, endPoint) * duration, valid, startPoint, endPoint, shown, coloring);
854        }
855    }
856    
857    public static class GlowBallEffect extends PanelEffect
858    {
859        /**
860         * This will change over the course of the effect's duration, and includes 16 overlapping faint glowing areas.
861         */
862        public Coord[] centers = new Coord[16];
863
864        /**
865         * Where the glow effect should travel towards as a whole.
866         */
867        public Coord end;
868        /**
869         * Normally you should set this in the constructor, and not change it later.
870         */
871        public int radius = 3;
872        /**
873         * The default glow ball color is medium-light blue.
874         */
875        public float color = SColor.CW_AZURE.toFloatBits();
876        /**
877         * The internal representation of how affected each cell is by the glow, based on proximity to center.
878         * This always has 16 light maps, but many will be identical and only calculated once.
879         */
880        public double[][][] lightMaps = new double[16][][];
881        private double[][] resMap;
882        /**
883         * The raw list of Coords that might be affected by the glow; may include some cells that aren't going to
884         * show as glowing (it usually has some false positives), but shouldn't exclude any cells that should show as
885         * such (no false negatives). You can edit this if you need to, but it isn't recommended.
886         */
887        public List<Coord> affected;
888        /**
889         * Constructs a GlowBallEffect with explicit settings for some fields. The valid cells this can affect will be
890         * the full expanse of the IPackedColorPanel. The duration will be 1 second.
891         * @param targeting the IPackedColorPanel to affect
892         * @param start the starting point for the glow ball(s)
893         * @param end the ending point for the glow ball(s)
894         * @param radius the radius of the explosion, in cells
895         */
896
897        public GlowBallEffect(IPackedColorPanel targeting, Coord start, Coord end, int radius)
898        {
899            this(targeting, 1f, start, end, radius);
900        }
901        /**
902         * Constructs a GlowBallEffect with explicit settings for some fields. The valid cells this can affect will be
903         * the full expanse of the IPackedColorPanel.
904         * @param targeting the IPackedColorPanel to affect
905         * @param duration the duration of this PanelEffect in seconds, as a float
906         * @param start the starting point for the glow ball(s)
907         * @param end the ending point for the glow ball(s)
908         * @param radius the radius of the explosion, in cells
909         */
910        public GlowBallEffect(IPackedColorPanel targeting, float duration, Coord start, Coord end, int radius)
911        {
912            super(targeting, duration);
913            Arrays.fill(centers, start);
914            this.end = end;
915            this.radius = radius;
916            resMap = new double[validCells.width][validCells.height];
917            lightMaps[0] = new double[validCells.width][validCells.height];
918            FOV.reuseFOV(resMap, lightMaps[0], start.x, start.y, radius + 0.5);
919            for (int i = 1; i < 16; i++) {
920                lightMaps[i] = lightMaps[0];
921            }
922            affected = Radius.inCircle(start.x, start.y, radius, false, validCells.width, validCells.height);
923        }
924        /**
925         * Constructs a GlowBallEffect with explicit settings for some fields. The valid cells this can affect will be
926         * the full expanse of the IPackedColorPanel.
927         * @param targeting the IPackedColorPanel to affect
928         * @param duration the duration of this PanelEffect in seconds, as a float
929         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
930         * @param start the starting point for the glow ball(s)
931         * @param end the ending point for the glow ball(s)
932         * @param radius the radius of the explosion, in cells
933         */
934        public GlowBallEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord start, Coord end, int radius)
935        {
936            super(targeting, duration, valid);
937            Arrays.fill(centers, start);
938            this.end = end;
939            this.radius = radius;
940
941            resMap = ArrayTools.fill(1.0, validCells.width, validCells.height);
942            validCells.writeDoublesInto(resMap, 0.0);
943            lightMaps[0] = new double[validCells.width][validCells.height];
944            FOV.reuseFOV(resMap, lightMaps[0], start.x, start.y, radius + 0.5);
945            validCells.not().writeDoublesInto(lightMaps[0], 0.0);
946            validCells.not();
947            for (int i = 1; i < 16; i++) {
948                lightMaps[i] = lightMaps[0];
949            }
950            affected = Radius.inCircle(start.x, start.y, radius, false, validCells.width, validCells.height);
951        }
952
953        /**
954         * Constructs a GlowBallEffect with explicit settings for some fields. The valid cells this can affect will be
955         * the full expanse of the IPackedColorPanel.
956         * @param targeting the IPackedColorPanel to affect
957         * @param duration the duration of this PanelEffect in seconds, as a float
958         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
959         * @param start the starting point for the glow ball(s)
960         * @param end the ending point for the glow ball(s)
961         * @param radius the radius of the explosion, in cells
962         */
963        public GlowBallEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord start, Coord end, int radius, Color coloring)
964        {
965            this(targeting, duration, valid, start, end, radius, coloring.toFloatBits());
966        }
967
968        /**
969         * Constructs a GlowBallEffect with explicit settings for some fields. The valid cells this can affect will be
970         * the full expanse of the IPackedColorPanel.
971         * @param targeting the IPackedColorPanel to affect
972         * @param duration the duration of this PanelEffect in seconds, as a float
973         * @param valid the valid cells that can be changed by this PanelEffect, as a GreasedRegion
974         * @param start the starting point for the glow ball(s)
975         * @param end the ending point for the glow ball(s)
976         * @param radius the radius of the explosion, in cells
977         */
978        public GlowBallEffect(IPackedColorPanel targeting, float duration, GreasedRegion valid, Coord start, Coord end, int radius, float coloring)
979        {
980            this(targeting, duration, valid, start, end, radius);
981            color = coloring;
982        }
983        private static float adjust(final float x, final int amt) { return ((x * (16 + amt)) + ((16 - amt) * x * x * (3f - 2f * x))) * 0.03125f; }
984        /**
985         * Called each frame.
986         *
987         * @param percent The percentage of completion for this action, growing from 0 to 1 over the duration. If
988         *                {@link #setReverse(boolean) reversed}, this will shrink from 1 to 0.
989         */
990        @Override
991        protected void update(float percent) {
992            affected.clear();
993            Radius.inCircle(centers[0].x, centers[0].y, radius, false, validCells.width, validCells.height, affected);
994            int len = affected.size();
995            Coord c;
996            float f, light;
997            Coord prev = centers[0];
998            for (int i = 0; i < 16; i++) {
999                centers[i] = centers[i].interpolate(end, adjust(percent, i));
1000            }
1001            validCells.not();
1002            if(!prev.equals(centers[0]))
1003            {
1004                FOV.reuseFOV(resMap, lightMaps[0], centers[0].x, centers[0].y, radius + 0.5);
1005                validCells.writeDoublesInto(lightMaps[0], 0.0);
1006            }
1007            for (int i = 1; i < 16; i++) {
1008                if(!centers[i-1].equals(centers[i]))
1009                {
1010                    FOV.reuseFOV(resMap, lightMaps[i], centers[i].x, centers[i].y, radius + 0.5);
1011                    validCells.writeDoublesInto(lightMaps[i], 0.0);
1012                }
1013                else
1014                {
1015                    lightMaps[i] = lightMaps[i-1];
1016                }
1017            }
1018            validCells.not();
1019            for (int i = 0; i < len; i++) {
1020                c = affected.get(i);
1021                for (int j = 0; j < 16; j++) {
1022                    if ((light = (float) lightMaps[j][c.x][c.y]) <= 0f)
1023                        continue;
1024                    target.blend(c.x, c.y, color,  light * 0.0625f);
1025                }
1026            }
1027        }
1028    }
1029
1030
1031    public static Interpolation fastInSlowMidFastOut = new Interpolation() {
1032        private final float value = 2, power = 3;
1033        @Override
1034        public float apply(float a) {
1035            if (a <= 0.5f) return (1 - ((float)Math.pow(value, -power * (a * 2)) - 0.125f) * 1.1428572f) * 0.5f;
1036            return (1 + (float) Math.pow(value, power * (a * 2 - 2)) - 0.25f) * 0.5714286f;
1037        }
1038    };
1039
1040    /**
1041     * Convenience method to make a ProjectileEffect or SteadyProjectileEffect take an "arc-like" path toward the
1042     * target, where it is fast at the beginning and end of its motion and is reaching the height of its arc at the
1043     * center, before triggering another Action when the projectile stops (often this might be an
1044     * {@link PanelEffect.ExplosionEffect}, but could be any scene2d Action).
1045     * @param projectile a {@link PanelEffect.ProjectileEffect} (or a subclass of it) to run as the first step
1046     * @param result an {@link Action} to run after the ProjectileEffect completes
1047     * @return an Action that can be added to a scene2d Actor, such as a SquidLayers or SparseLayers
1048     */
1049    public static Action makeGrenadeEffect(ProjectileEffect projectile, Action result)
1050    {
1051        projectile.useArcPathInterpolation();
1052        return Actions.sequence(projectile, result);
1053    }
1054}