001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.Gdx;
004import com.badlogic.gdx.graphics.Color;
005import com.badlogic.gdx.graphics.g2d.Batch;
006import com.badlogic.gdx.graphics.g2d.TextureRegion;
007import com.badlogic.gdx.math.MathUtils;
008import com.badlogic.gdx.scenes.scene2d.Actor;
009import com.badlogic.gdx.scenes.scene2d.Group;
010import com.badlogic.gdx.scenes.scene2d.actions.Actions;
011import com.badlogic.gdx.utils.Align;
012import squidpony.IColorCenter;
013import squidpony.panel.IColoredString;
014import squidpony.panel.ISquidPanel;
015import squidpony.squidgrid.Direction;
016import squidpony.squidmath.Coord;
017import squidpony.squidmath.StatefulRNG;
018
019import java.util.*;
020
021/**
022 * Displays text and images in a grid pattern. Supports basic animations.
023 * 
024 * Grid width and height settings are in terms of number of cells. Cell width and height
025 * are in terms of number of pixels.
026 *
027 * When text is placed, the background color is set separately from the foreground character. When moved, only the
028 * foreground character is moved.
029 *
030 * @author Eben Howard - http://squidpony.com - howard@squidpony.com
031 */
032public class SquidPanel extends Group implements ISquidPanel<Color> {
033
034    public float DEFAULT_ANIMATION_DURATION = 0.12F;
035    protected int animationCount = 0;
036    protected Color defaultForeground = Color.WHITE;
037    protected IColorCenter<Color> scc;
038    protected final int cellWidth, cellHeight;
039    protected int gridWidth, gridHeight, gridOffsetX = 0, gridOffsetY = 0;
040    protected final String[][] contents;
041    protected final Color[][] colors;
042    protected Color lightingColor = SColor.WHITE;
043    protected final TextCellFactory textFactory;
044    protected float xOffset, yOffset;
045    protected LinkedHashSet<AnimatedEntity> animatedEntities;
046    protected boolean distanceField = false;
047
048    /**
049     * Creates a bare-bones panel with all default values for text rendering.
050     *
051     * @param gridWidth the number of cells horizontally
052     * @param gridHeight the number of cells vertically
053     */
054    public SquidPanel(int gridWidth, int gridHeight) {
055        this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont());
056    }
057
058    /**
059     * Creates a panel with the given grid and cell size. Uses a default square font.
060     *
061     * @param gridWidth the number of cells horizontally
062     * @param gridHeight the number of cells vertically
063     * @param cellWidth the number of horizontal pixels in each cell
064     * @param cellHeight the number of vertical pixels in each cell
065     */
066    public SquidPanel(int gridWidth, int gridHeight, int cellWidth, int cellHeight) {
067        this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont().width(cellWidth).height(cellHeight));
068    }
069
070    /**
071     * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
072     * are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
073     *
074     * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
075     * then a default one will be created and initialized.
076     *
077     * @param gridWidth the number of cells horizontally
078     * @param gridHeight the number of cells vertically
079     * @param factory the factory to use for cell rendering
080     */
081    public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory) {
082        this(gridWidth, gridHeight, factory, DefaultResources.getSCC());
083    }
084
085    /**
086     * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
087     * are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
088     *
089     * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
090     * then a default one will be created and initialized.
091     *
092     * @param gridWidth the number of cells horizontally
093     * @param gridHeight the number of cells vertically
094     * @param factory the factory to use for cell rendering
095     * @param center
096     *                  The color center to use. Can be {@code null}, but then must be set later on with
097     *          {@link #setColorCenter(IColorCenter)}.
098     */
099    public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center) {
100        this(gridWidth, gridHeight, factory, center, 0f, 0f);
101    }
102
103    /**
104     * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
105     * are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
106     *
107     * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
108     * then a default one will be created and initialized.
109     *
110     * @param gridWidth the number of cells horizontally
111     * @param gridHeight the number of cells vertically
112     * @param factory the factory to use for cell rendering
113     * @param center
114     *                  The color center to use. Can be {@code null}, but then must be set later on with
115     *          {@link #setColorCenter(IColorCenter)}.
116     */
117    public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center,
118                      float xOffset, float yOffset) {
119        this.gridWidth = gridWidth;
120        this.gridHeight = gridHeight;
121        if(center == null)
122            scc = DefaultResources.getSCC();
123        else
124            scc = center;
125
126        if (factory == null) {
127            textFactory = new TextCellFactory();
128        }
129        else
130            textFactory = factory;
131        if (!textFactory.initialized()) {
132            textFactory.initByFont();
133        }
134
135        cellWidth = MathUtils.round(textFactory.actualCellWidth);
136        cellHeight = MathUtils.round(textFactory.actualCellHeight);
137
138        contents = new String[gridWidth][gridHeight];
139        colors = new Color[gridWidth][gridHeight];
140        for (int i = 0; i < gridWidth; i++) {
141            Arrays.fill(colors[i], scc.filter(Color.CLEAR));
142        }
143
144
145        int w = gridWidth * cellWidth;
146        int h = gridHeight * cellHeight;
147        this.xOffset = xOffset;
148        this.yOffset = yOffset;
149        setSize(w, h);
150        animatedEntities = new LinkedHashSet<>();
151    }
152
153    /**
154     * Places the given characters into the grid starting at 0,0.
155     *
156     * @param chars
157     */
158    public void put(char[][] chars) {
159        put(0, 0, chars);
160    }
161
162        @Override
163        public void put(/* @Nullable */char[][] chars, Color[][] foregrounds) {
164                if (chars == null) {
165                        /* Only colors to put */
166                        final int width = foregrounds.length;
167                        final int height = width == 0 ? 0 : foregrounds[0].length;
168                        for (int x = 0; x < width; x++) {
169                                for (int y = 0; y < height; y++)
170                                        put(x, y, foregrounds[x][y]);
171                        }
172                } else
173                        put(0, 0, chars, foregrounds);
174        }
175
176    public void put(char[][] chars, int[][] indices, ArrayList<Color> palette) {
177        put(0, 0, chars, indices, palette);
178    }
179
180    public void put(int xOffset, int yOffset, char[][] chars) {
181        put(xOffset, yOffset, chars, defaultForeground);
182    }
183
184    public void put(int xOffset, int yOffset, char[][] chars, Color[][] foregrounds) {
185        for (int x = xOffset; x < xOffset + chars.length; x++) {
186            for (int y = yOffset; y < yOffset + chars[0].length; y++) {
187                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
188                    put(x, y, chars[x - xOffset][y - yOffset], foregrounds[x - xOffset][y - yOffset]);
189                }
190            }
191        }
192    }
193
194    public void put(int xOffset, int yOffset, char[][] chars, int[][] indices, ArrayList<Color> palette) {
195        for (int x = xOffset; x < xOffset + chars.length; x++) {
196            for (int y = yOffset; y < yOffset + chars[0].length; y++) {
197                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
198                    put(x, y, chars[x - xOffset][y - yOffset], palette.get(indices[x - xOffset][y - yOffset]));
199                }
200            }
201        }
202    }
203
204    public void put(int xOffset, int yOffset, Color[][] foregrounds) {
205        for (int x = xOffset; x < xOffset + foregrounds.length; x++) {
206            for (int y = yOffset; y < yOffset + foregrounds[0].length; y++) {
207                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
208                    put(x, y, '\0', foregrounds[x - xOffset][y - yOffset]);
209                }
210            }
211        }
212    }
213
214    public void put(int xOffset, int yOffset, int[][] indices, ArrayList<Color> palette) {
215        for (int x = xOffset; x < xOffset + indices.length; x++) {
216            for (int y = yOffset; y < yOffset + indices[0].length; y++) {
217                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
218                    put(x, y, '\0', palette.get(indices[x - xOffset][y - yOffset]));
219                }
220            }
221        }
222    }
223
224    public void put(int xOffset, int yOffset, char[][] chars, Color foreground) {
225        for (int x = xOffset; x < xOffset + chars.length; x++) {
226            for (int y = yOffset; y < yOffset + chars[0].length; y++) {
227                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
228                    put(x, y, chars[x - xOffset][y - yOffset], foreground);
229                }
230            }
231        }
232    }
233
234    /**
235     * Puts the given string horizontally with the first character at the given offset.
236     *
237     * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
238     * the grid size) will not be shown but will not cause any malfunctions.
239     *
240     * Will use the default color for this component to draw the characters.
241     *
242     * @param xOffset the x coordinate of the first character
243     * @param yOffset the y coordinate of the first character
244     * @param string the characters to be displayed
245     */
246    public void put(int xOffset, int yOffset, String string) {
247        put(xOffset, yOffset, string, defaultForeground);
248    }
249
250        @Override
251        public void put(int xOffset, int yOffset, IColoredString<? extends Color> cs) {
252                int x = xOffset;
253                for (IColoredString.Bucket<? extends Color> fragment : cs) {
254                        final String s = fragment.getText();
255                        final Color color = fragment.getColor();
256                        put(x, yOffset, s, color == null ? getDefaultForegroundColor() : scc.filter(color));
257                        x += s.length();
258                }
259        }
260
261    @Override
262    public void put(int xOffset, int yOffset, String string, Color foreground) {
263        if (string.length() == 1) {
264            put(xOffset, yOffset, string.charAt(0), foreground);
265        }
266        else
267        {
268            char[][] temp = new char[string.length()][1];
269            for (int i = 0; i < string.length(); i++) {
270                temp[i][0] = string.charAt(i);
271            }
272            put(xOffset, yOffset, temp, foreground);
273        }
274    }
275    public void put(int xOffset, int yOffset, String string, Color foreground, float colorMultiplier) {
276        if (string.length() == 1) {
277            put(xOffset, yOffset, string.charAt(0), foreground, colorMultiplier);
278        }
279        else
280        {
281            char[][] temp = new char[string.length()][1];
282            for (int i = 0; i < string.length(); i++) {
283                temp[i][0] = string.charAt(i);
284            }
285            put(xOffset, yOffset, temp, foreground, colorMultiplier);
286        }
287    }
288
289    public void put(int xOffset, int yOffset, char[][] chars, Color foreground, float colorMultiplier) {
290        for (int x = xOffset; x < xOffset + chars.length; x++) {
291            for (int y = yOffset; y < yOffset + chars[0].length; y++) {
292                if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
293                    put(x, y, chars[x - xOffset][y - yOffset], foreground, colorMultiplier);
294                }
295            }
296        }
297    }
298
299    /**
300     * Puts the given string horizontally or optionally vertically, with the first character at the given offset.
301     *
302     * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
303     * the grid size) will not be shown but will not cause any malfunctions.
304     *
305     * Will use the default color for this component to draw the characters.
306     *
307     * @param xOffset the x coordinate of the first character
308     * @param yOffset the y coordinate of the first character
309     * @param string the characters to be displayed
310     * @param vertical true if the text should be written vertically, from top to bottom
311     */
312    public void placeVerticalString(int xOffset, int yOffset, String string, boolean vertical) {
313        put(xOffset, yOffset, string, defaultForeground, vertical);
314    }
315
316    /**
317     * Puts the given string horizontally or optionally vertically, with the first character at the given offset.
318     *
319     * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
320     * the grid size) will not be shown but will not cause any malfunctions.
321     *
322     * @param xOffset the x coordinate of the first character
323     * @param yOffset the y coordinate of the first character
324     * @param string the characters to be displayed
325     * @param foreground the color to draw the characters
326     * @param vertical true if the text should be written vertically, from top to bottom
327     */
328    public void put(int xOffset, int yOffset, String string, Color foreground, boolean vertical) {
329        if (vertical) {
330            put(xOffset, yOffset, new char[][]{string.toCharArray()}, foreground);
331        } else {
332            put(xOffset, yOffset, string, foreground);
333        }
334    }
335
336    /**
337     * Erases the entire panel, leaving only a transparent space.
338     */
339    public void erase() {
340        for (int i = 0; i < contents.length; i++) {
341            Arrays.fill(contents[i], "\0");
342            Arrays.fill(colors[i], Color.CLEAR);
343            /*
344            for (int j = 0; j < contents[i].length; j++) {
345                contents[i][j] = "\0";
346                colors[i][j] = Color.CLEAR;
347            }
348            */
349        }
350    }
351
352    @Override
353        public void clear(int x, int y) {
354        put(x, y, Color.CLEAR);
355    }
356
357    @Override
358        public void put(int x, int y, Color color) {
359        put(x, y, '\0', color);
360    }
361
362    public void put(int x, int y, Color color, float colorMultiplier) {
363        put(x, y, '\0', color, colorMultiplier);
364    }
365
366    @Override
367        public void put(int x, int y, char c) {
368        put(x, y, c, defaultForeground);
369    }
370
371    /**
372     * Takes a unicode codepoint for input.
373     *
374     * @param x
375     * @param y
376     * @param code
377     */
378    public void put(int x, int y, int code) {
379        put(x, y, code, defaultForeground);
380    }
381
382    public void put(int x, int y, int c, Color color) {
383        put(x, y, String.valueOf(Character.toChars(c)), color);
384    }
385
386    public void put(int x, int y, int index, ArrayList<Color> palette) {
387        put(x, y, palette.get(index));
388    }
389
390    public void put(int x, int y, char c, int index, ArrayList<Color> palette) {
391        put(x, y, c, palette.get(index));
392    }
393
394    /**
395     * Takes a unicode codepoint for input.
396     *
397     * @param x
398     * @param y
399     * @param c
400     * @param color
401     */
402    @Override
403    public void put(int x, int y, char c, Color color) {
404        if (x < 0 || x >= gridWidth || y < 0 || y >= gridHeight) {
405            return;//skip if out of bounds
406        }
407        contents[x][y] = String.valueOf(c);
408        colors[x][y] = scc.filter(color);
409    }
410
411        /**
412         * @throws UnsupportedOperationException
413         *             If the backing {@link IColorCenter} isn't an instance of
414         *             {@link SquidColorCenter}.
415         */
416        public void put(int x, int y, char c, Color color, float colorMultiplier) {
417        if (x < 0 || x >= gridWidth || y < 0 || y >= gridHeight) {
418            return;//skip if out of bounds
419        }
420        contents[x][y] = String.valueOf(c);
421                if (!(scc instanceof SquidColorCenter))
422                        throw new UnsupportedOperationException("This method required the color center to be a "
423                                        + SquidColorCenter.class.getSimpleName());
424                colors[x][y] = ((SquidColorCenter) scc).lerp(color, lightingColor, colorMultiplier);
425        }
426
427    @Override
428        public int cellWidth() {
429        return cellWidth;
430    }
431
432    @Override
433        public int cellHeight() {
434        return cellHeight;
435    }
436
437    @Override
438        public int gridHeight() {
439        return gridHeight;
440    }
441
442    @Override
443        public int gridWidth() {
444        return gridWidth;
445    }
446
447        /**
448         * @return The {@link TextCellFactory} backing {@code this}.
449         */
450        public TextCellFactory getTextCellFactory() {
451                return textFactory;
452        }
453
454    /**
455     * Sets the size of the text in this SquidPanel (but not the size of the cells) to the given width and height in
456     * pixels (which may be stretched by viewports later on, if your program uses them).
457     * @param wide the width of a glyph in pixels
458     * @param high the height of a glyph in pixels
459     * @return this for chaining
460     */
461    public SquidPanel setTextSize(int wide, int high)
462    {
463        textFactory.tweakHeight(high).tweakWidth(wide).initBySize();
464        //textFactory.setSmoothingMultiplier((3f + Math.max(cellWidth * 1f / wide, cellHeight * 1f / high)) / 4f);
465        return this;
466    }
467
468    @Override
469    public void draw (Batch batch, float parentAlpha) {
470        /*if(batch.isDrawing()) {
471            batch.end();
472            batch.begin();
473        }*/
474        textFactory.configureShader(batch);
475        Color tmp;
476
477        for (int x = gridOffsetX; x < gridWidth; x++) {
478            for (int y = gridOffsetY; y < gridHeight; y++) {
479                tmp = scc.filter(colors[x][y]);
480                textFactory.draw(batch, contents[x][y], tmp, xOffset + /*- getX() + */1f * x * cellWidth,
481                        yOffset + /*- getY() + */1f * (gridHeight - y) * cellHeight + 1f);
482            }
483        }
484        super.draw(batch, parentAlpha);
485        for(AnimatedEntity ae : animatedEntities)
486        {
487            ae.actor.act(Gdx.graphics.getDeltaTime());
488        }
489    }
490
491    /**
492     * Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end()
493     * @param batch Must have start() called already but not stop() yet during this frame.
494     * @param parentAlpha This can be assumed to be 1.0f if you don't know it
495     * @param ae The AnimatedEntity to draw; the position to draw ae is stored inside it.
496     */
497    public void drawActor(Batch batch, float parentAlpha, AnimatedEntity ae)
498    {
499            ae.actor.draw(batch, parentAlpha);
500    }
501
502    @Override
503        public void setDefaultForeground(Color defaultForeground) {
504        this.defaultForeground = defaultForeground;
505    }
506
507        @Override
508        public Color getDefaultForegroundColor() {
509                return defaultForeground;
510        }
511
512    public AnimatedEntity getAnimatedEntityByCell(int x, int y) {
513        for(AnimatedEntity ae : animatedEntities)
514        {
515            if(ae.gridX == x && ae.gridY == y)
516                return ae;
517        }
518        return  null;
519    }
520
521    /**
522     * Create an AnimatedEntity at position x, y, using the char c in the given color.
523     * @param x
524     * @param y
525     * @param c
526     * @param color
527     * @return
528     */
529    public AnimatedEntity animateActor(int x, int y, char c, Color color)
530    {
531        return animateActor(x, y, false, String.valueOf(c), color);
532        /*
533        Actor a = textFactory.makeActor("" + c, color);
534        a.setName("" + c);
535        a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
536
537        AnimatedEntity ae = new AnimatedEntity(a, x, y);
538        animatedEntities.add(ae);
539        return ae;
540        */
541    }
542
543    /**
544     * Create an AnimatedEntity at position x, y, using the char c in the given color. If doubleWidth is true, treats
545     * the char c as the left char to be placed in a grid of 2-char cells.
546     * @param x
547     * @param y
548     * @param doubleWidth
549     * @param c
550     * @param color
551     * @return
552     */
553    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Color color)
554    {
555        return animateActor(x, y, doubleWidth, String.valueOf(c), color);
556        /*
557        Actor a = textFactory.makeActor("" + c, color);
558        a.setName("" + c);
559        if(doubleWidth)
560            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
561        else
562            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
563
564        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
565        animatedEntities.add(ae);
566        return ae;
567        */
568    }
569
570    /**
571     * Create an AnimatedEntity at position x, y, using the String s in the given color.
572     * @param x
573     * @param y
574     * @param s
575     * @param color
576     * @return
577     */
578    public AnimatedEntity animateActor(int x, int y, String s, Color color)
579    {
580        return animateActor(x, y, false, s, color);
581        /*
582        Actor a = textFactory.makeActor(s, color);
583        a.setName(s);
584        a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
585
586        AnimatedEntity ae = new AnimatedEntity(a, x, y);
587        animatedEntities.add(ae);
588        return ae;
589        */
590    }
591
592    /**
593     * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
594     * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
595     * @param x
596     * @param y
597     * @param doubleWidth
598     * @param s
599     * @param color
600     * @return
601     */
602    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Color color)
603    {
604        Actor a = textFactory.makeActor(s, color);
605        a.setName(s);
606        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
607        /*
608        if(doubleWidth)
609            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
610        else
611            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
612        */
613        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
614        animatedEntities.add(ae);
615        return ae;
616    }
617    /**
618     * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
619     * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
620     * @param x
621     * @param y
622     * @param doubleWidth
623     * @param s
624     * @param colors
625     * @return
626     */
627    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors)
628    {
629        Actor a = textFactory.makeActor(s, colors);
630        a.setName(s);
631        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
632        /*
633        if(doubleWidth)
634            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
635        else
636            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
637        */
638        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
639        animatedEntities.add(ae);
640        return ae;
641    }
642    /**
643     * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
644     * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
645     * @param x
646     * @param y
647     * @param doubleWidth
648     * @param s
649     * @param colors
650     * @param loopTime
651     * @return
652     */
653    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors, float loopTime)
654    {
655        Actor a = textFactory.makeActor(s, colors, loopTime);
656        a.setName(s);
657        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
658        /*
659        if(doubleWidth)
660            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
661        else
662            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
663        */
664        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
665        animatedEntities.add(ae);
666        return ae;
667    }
668
669    /**
670     * Create an AnimatedEntity at position x, y, using the char c with a color looked up by index in palette.
671     * @param x
672     * @param y
673     * @param c
674     * @param index
675     * @param palette
676     * @return
677     */
678    public AnimatedEntity animateActor(int x, int y, char c, int index, ArrayList<Color> palette)
679    {
680        return animateActor(x, y, c, palette.get(index));
681    }
682
683    /**
684     * Create an AnimatedEntity at position x, y, using the String s with a color looked up by index in palette.
685     * @param x
686     * @param y
687     * @param s
688     * @param index
689     * @param palette
690     * @return
691     */
692    public AnimatedEntity animateActor(int x, int y, String s, int index, ArrayList<Color> palette)
693    {
694        return animateActor(x, y, s, palette.get(index));
695    }
696
697    /**
698     * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be
699     * stretched to fit one cell.
700     * @param x
701     * @param y
702     * @param texture
703     * @return
704     */
705    public AnimatedEntity animateActor(int x, int y, TextureRegion texture)
706    {
707        return animateActor(x, y, false, texture, Color.WHITE);
708        /*
709        Actor a = textFactory.makeActor(texture, Color.WHITE);
710        a.setName("");
711        a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
712
713        AnimatedEntity ae = new AnimatedEntity(a, x, y);
714        animatedEntities.add(ae);
715        return ae;
716        */
717    }
718
719    /**
720     * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
721     * stretched to fit one cell.
722     * @param x
723     * @param y
724     * @param texture
725     * @param color
726     * @return
727     */
728    public AnimatedEntity animateActor(int x, int y, TextureRegion texture, Color color)
729    {
730        return animateActor(x, y, false, texture, color);
731        /*
732        Actor a = textFactory.makeActor(texture, color);
733        a.setName("");
734        a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
735
736        AnimatedEntity ae = new AnimatedEntity(a, x, y);
737        animatedEntities.add(ae);
738        return ae;
739        */
740    }
741
742    /**
743     * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be
744     * stretched to fit one cell, or two cells if doubleWidth is true.
745     * @param x
746     * @param y
747     * @param doubleWidth
748     * @param texture
749     * @return
750     */
751    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture)
752    {
753        return animateActor(x, y, doubleWidth, texture, Color.WHITE);
754        /*
755        Actor a = textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
756
757        a.setName("");
758        if(doubleWidth)
759            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
760        else
761            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
762
763        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
764        animatedEntities.add(ae);
765        return ae;
766        */
767    }
768
769    /**
770     * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
771     * stretched to fit one cell, or two cells if doubleWidth is true.
772     * @param x
773     * @param y
774     * @param doubleWidth
775     * @param texture
776     * @param color
777     * @return
778     */
779    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Color color) {
780        Actor a = textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
781        a.setName("");
782        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
783        /*
784        if (doubleWidth)
785            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
786        else
787            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
788        */
789        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
790        animatedEntities.add(ae);
791        return ae;
792    }
793
794    /**
795     * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which, if and only
796     * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false,
797     * this will preserve the existing size of texture.
798     * @param x
799     * @param y
800     * @param doubleWidth
801     * @param stretch
802     * @param texture
803     * @return
804     */
805    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture)
806    {
807        Actor a = (stretch)
808                ? textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight)
809                : textFactory.makeActor(texture, Color.WHITE, texture.getRegionWidth(), texture.getRegionHeight());
810        a.setName("");
811        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
812        /*
813        if(doubleWidth)
814            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
815        else
816            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight  - textFactory.getDescent() + getY());
817        */
818        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
819        animatedEntities.add(ae);
820        return ae;
821    }
822
823    /**
824     * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which, if and only
825     * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false,
826     * this will preserve the existing size of texture.
827     * @param x
828     * @param y
829     * @param doubleWidth
830     * @param stretch
831     * @param texture
832     * @param color
833     * @return
834     */
835    public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture, Color color) {
836
837        Actor a = (stretch)
838                ? textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight)
839                : textFactory.makeActor(texture, color, texture.getRegionWidth(), texture.getRegionHeight());
840        a.setName("");
841        a.setPosition(adjustX(x, doubleWidth), adjustY(y));
842        /*
843        if (doubleWidth)
844            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight  - textFactory.getDescent() + getY());
845        else
846            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight  - textFactory.getDescent() + getY());
847            */
848        AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
849        animatedEntities.add(ae);
850        return ae;
851    }
852
853    /**
854     * Created an Actor from the contents of the given x,y position on the grid.
855     * @param x
856     * @param y
857     * @return
858     */
859    public Actor cellToActor(int x, int y)
860    {
861        return cellToActor(x, y, false);
862    }
863
864    /**
865     * Created an Actor from the contents of the given x,y position on the grid.
866     * @param x
867     * @param y
868     * @param doubleWidth
869     * @return
870     */
871    public Actor cellToActor(int x, int y, boolean doubleWidth)
872    {
873        if(contents[x][y] == null || contents[x][y].equals(""))
874            return null;
875
876        Actor a = textFactory.makeActor(contents[x][y], scc.filter(colors[x][y]));
877        a.setName(contents[x][y]);
878        a.setPosition(adjustX(x, doubleWidth) - getX() * 2, adjustY(y) - getY() * 2); // - textFactory.lineHeight * 0.25f
879
880        /*
881        if(doubleWidth)
882            a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight  - textFactory.getDescent() + getY());
883        else
884            a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight  - textFactory.getDescent() + getY());
885         */
886
887        addActor(a);
888
889        //contents[x][y] = "";
890        return a;
891    }
892
893    public float adjustX(float x, boolean doubleWidth)
894    {
895        if(doubleWidth)
896            return x * 2 * cellWidth + getX();
897        else
898            return x * cellWidth + getX();
899    }
900
901    public float adjustY(float y)
902    {
903        return (gridHeight - y - 1) * cellHeight + getY() + 1; // - textFactory.lineHeight //textFactory.lineTweak * 3f
904        //return (gridHeight - y - 1) * cellHeight + textFactory.getDescent() * 3 / 2f + getY();
905    }
906
907    /*
908    public void startAnimation(Actor a, int oldX, int oldY)
909    {
910        Coord tmp = Coord.get(oldX, oldY);
911
912        tmp.x = Math.round(a.getX() / cellWidth);
913        tmp.y = gridHeight - Math.round(a.getY() / cellHeight) - 1;
914        if(tmp.x >= 0 && tmp.x < gridWidth && tmp.y > 0 && tmp.y < gridHeight)
915        {
916        }
917    }
918    */
919    public void recallActor(Actor a)
920    {
921        animationCount--;
922        int x = Math.round((a.getX() - getX()) / cellWidth),
923             y = gridHeight - (int)((a.getY() - getY()) / cellHeight) - 1;
924        if(x < 0 || y < 0 || x >= contents.length || y >= contents[x].length)
925            return;
926        contents[x][y] = a.getName();
927        removeActor(a);
928    }
929    public void recallActor(AnimatedEntity ae)
930    {
931        if(ae.doubleWidth)
932            ae.gridX = Math.round((ae.actor.getX() - getX()) / (2 * cellWidth));
933        else
934            ae.gridX = Math.round((ae.actor.getX() - getX()) / cellWidth);
935        ae.gridY = gridHeight - (int)((ae.actor.getY() - getY()) / cellHeight) - 1;
936        ae.animating = false;
937        animationCount--;
938    }
939
940    /**
941     * Start a bumping animation in the given direction that will last duration seconds.
942     * @param ae an AnimatedEntity returned by animateActor()
943     * @param direction
944     * @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f
945     */
946    public void bump(final AnimatedEntity ae, Direction direction, float duration)
947    {
948        final Actor a = ae.actor;
949        final float x = adjustX(ae.gridX, ae.doubleWidth),
950                y = adjustY(ae.gridY);
951        // ae.gridX * cellWidth + (int)getX(),
952        // (gridHeight - ae.gridY - 1) * cellHeight - 1 + (int)getY();
953        if(a == null || ae.animating) return;
954        duration = clampDuration(duration);
955        animationCount++;
956        ae.animating = true;
957        a.addAction(Actions.sequence(
958                Actions.moveToAligned(x + (direction.deltaX / 3F) * ((ae.doubleWidth) ? 2F : 1F), y + direction.deltaY / 3F,
959                        Align.center, duration * 0.35F),
960                Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.65F),
961                Actions.delay(duration, Actions.run(new Runnable() {
962                    @Override
963                    public void run() {
964                        recallActor(ae);
965                    }
966                }))));
967
968    }
969    /**
970     * Start a bumping animation in the given direction that will last duration seconds.
971     * @param x
972     * @param y
973     * @param direction
974     * @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f
975     */
976    public void bump(int x, int y, Direction direction, float duration)
977    {
978        final Actor a = cellToActor(x, y);
979        if(a == null) return;
980        duration = clampDuration(duration);
981        animationCount++;
982        float nextX = adjustX(x, false), nextY = adjustY(y);
983        /*
984        x *= cellWidth;
985        y = (gridHeight - y - 1);
986        y *= cellHeight;
987        y -= 1;
988        x +=  getX();
989        y +=  getY();
990        */
991        a.addAction(Actions.sequence(
992                Actions.moveToAligned(nextX + direction.deltaX / 3F, nextY + direction.deltaY / 3F,
993                        Align.center, duration * 0.35F),
994                Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.65F),
995                Actions.delay(duration, Actions.run(new Runnable() {
996                    @Override
997                    public void run() {
998                        recallActor(a);
999                    }
1000                }))));
1001
1002    }
1003
1004    /**
1005     * Starts a bumping animation in the direction provided.
1006     *
1007     * @param x
1008     * @param y
1009     * @param direction
1010     */
1011    public void bump(int x, int y, Direction direction) {
1012        bump(x, y, direction, DEFAULT_ANIMATION_DURATION);
1013    }
1014    /**
1015     * Starts a bumping animation in the direction provided.
1016     *
1017     * @param location
1018     * @param direction
1019     */
1020    public void bump(Coord location, Direction direction) {
1021        bump(location.x, location.y, direction, DEFAULT_ANIMATION_DURATION);
1022    }
1023    /**
1024     * Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of
1025     * seconds given by duration (often 0.12f or somewhere around there).
1026     * @param ae an AnimatedEntity returned by animateActor()
1027     * @param newX
1028     * @param newY
1029     * @param duration
1030     */
1031    public void slide(final AnimatedEntity ae, int newX, int newY, float duration)
1032    {
1033        final Actor a = ae.actor;
1034        final float nextX = adjustX(newX, ae.doubleWidth), nextY = adjustY(newY);
1035        //final int nextX = newX * cellWidth * ((ae.doubleWidth) ? 2 : 1) + (int)getX(), nextY = (gridHeight - newY - 1) * cellHeight - 1 + (int)getY();
1036        if(a == null || ae.animating) return;
1037        duration = clampDuration(duration);
1038        animationCount++;
1039        ae.animating = true;
1040        a.addAction(Actions.sequence(
1041                Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration),
1042                Actions.delay(duration, Actions.run(new Runnable() {
1043                    @Override
1044                    public void run() {
1045                        recallActor(ae);
1046                    }
1047                }))));
1048    }
1049
1050    /**
1051     * Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of
1052     * seconds given by duration (often 0.12f or somewhere around there).
1053     * @param x
1054     * @param y
1055     * @param newX
1056     * @param newY
1057     * @param duration
1058     */
1059    public void slide(int x, int y, int newX, int newY, float duration)
1060    {
1061        final Actor a = cellToActor(x, y);
1062        if(a == null) return;
1063        duration = clampDuration(duration);
1064        animationCount++;
1065        float nextX = adjustX(newX, false), nextY = adjustY(newY);
1066
1067        /*
1068        newX *= cellWidth;
1069        newY = (gridHeight - newY - 1);
1070        newY *= cellHeight;
1071        newY -= 1;
1072        x += getX();
1073        y += getY();
1074        */
1075        a.addAction(Actions.sequence(
1076                Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration),
1077                Actions.delay(duration, Actions.run(new Runnable() {
1078                    @Override
1079                    public void run() {
1080                        recallActor(a);
1081                    }
1082                }))));
1083    }
1084    /**
1085     * Starts a movement animation for the object at the given grid location at the default speed.
1086     *
1087     * @param start
1088     * @param end
1089     */
1090    public void slide(Coord start, Coord end) {
1091        slide(start.x, start.y, end.x, end.y, DEFAULT_ANIMATION_DURATION);
1092    }
1093
1094    /**
1095     * Starts a movement animation for the object at the given grid location at the default speed for one grid square in
1096     * the direction provided.
1097     *
1098     * @param start
1099     * @param direction
1100     */
1101    public void slide(Coord start, Direction direction) {
1102        slide(start.x, start.y, start.x + direction.deltaX, start.y + direction.deltaY, DEFAULT_ANIMATION_DURATION);
1103    }
1104
1105    /**
1106     * Starts a sliding movement animation for the object at the given location at the provided speed. The duration is
1107     * how many seconds should pass for the entire animation.
1108     *
1109     * @param start
1110     * @param end
1111     * @param duration
1112     */
1113    public void slide(Coord start, Coord end, float duration) {
1114        slide(start.x, start.y, end.x, end.y, duration);
1115    }
1116
1117    /**
1118     * Starts an wiggling animation for the object at the given location for the given duration in seconds.
1119     *
1120     * @param ae an AnimatedEntity returned by animateActor()
1121     * @param duration
1122     */
1123    public void wiggle(final AnimatedEntity ae, float duration) {
1124
1125        final Actor a = ae.actor;
1126        final float x = adjustX(ae.gridX, ae.doubleWidth), y = adjustY(ae.gridY);
1127        //final int x = ae.gridX * cellWidth * ((ae.doubleWidth) ? 2 : 1) + (int)getX(), y = (gridHeight - ae.gridY - 1) * cellHeight - 1 + (int)getY();
1128        if(a == null || ae.animating)
1129            return;
1130        duration = clampDuration(duration);
1131        ae.animating = true;
1132        animationCount++;
1133        StatefulRNG gRandom = DefaultResources.getGuiRandom();
1134        a.addAction(Actions.sequence(
1135                Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1136                        y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1137                        Align.bottomLeft, duration * 0.2F),
1138                Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1139                        y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1140                        Align.bottomLeft, duration * 0.2F),
1141                Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1142                        y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1143                        Align.bottomLeft, duration * 0.2F),
1144                Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1145                        y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1146                        Align.bottomLeft, duration * 0.2F),
1147                Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.2F),
1148                Actions.delay(duration, Actions.run(new Runnable() {
1149                    @Override
1150                    public void run() {
1151                        recallActor(ae);
1152                    }
1153                }))));
1154    }
1155    /**
1156     * Starts an wiggling animation for the object at the given location for the given duration in seconds.
1157     *
1158     * @param x
1159     * @param y
1160     * @param duration
1161     */
1162    public void wiggle(int x, int y, float duration) {
1163        final Actor a = cellToActor(x, y);
1164        if(a == null) return;
1165        duration = clampDuration(duration);
1166        animationCount++;
1167        float nextX = adjustX(x, false), nextY = adjustY(y);
1168        /*
1169        x *= cellWidth;
1170        y = (gridHeight - y - 1);
1171        y *= cellHeight;
1172        y -= 1;
1173        x +=  getX();
1174        y +=  getY();
1175        */
1176        StatefulRNG gRandom = DefaultResources.getGuiRandom();
1177        a.addAction(Actions.sequence(
1178                Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1179                        nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1180                        Align.bottomLeft, duration * 0.2F),
1181                Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1182                        nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1183                        Align.bottomLeft, duration * 0.2F),
1184                Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1185                        nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1186                        Align.bottomLeft, duration * 0.2F),
1187                Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
1188                        nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
1189                        Align.bottomLeft, duration * 0.2F),
1190                Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.2F),
1191                Actions.delay(duration, Actions.run(new Runnable() {
1192                    @Override
1193                    public void run() {
1194                        recallActor(a);
1195                    }
1196                }))));
1197    }
1198
1199    /**
1200     * Starts a tint animation for {@code ae} for the given {@code duration} in seconds.
1201     *
1202     * @param ae an AnimatedEntity returned by animateActor()
1203     * @param color
1204     * @param duration
1205     */
1206    public void tint(final AnimatedEntity ae, Color color, float duration) {
1207        final Actor a = ae.actor;
1208        if(a == null)
1209            return;
1210        duration = clampDuration(duration);
1211        ae.animating = true;
1212        animationCount++;
1213        Color ac = scc.filter(a.getColor());
1214        a.addAction(Actions.sequence(
1215                Actions.color(color, duration * 0.3f),
1216                Actions.color(ac, duration * 0.7f),
1217                Actions.delay(duration, Actions.run(new Runnable() {
1218                    @Override
1219                    public void run() {
1220                        recallActor(ae);
1221                    }
1222                }))));
1223    }
1224
1225    /**
1226         * Like {@link #tint(int, int, Color, float)}, but waits for {@code delay}
1227         * (in seconds) before performing it.
1228         */
1229    public void tint(float delay, int x, int y, Color color, float duration) {
1230        final Actor a = cellToActor(x, y);
1231        if(a == null)
1232            return;
1233        duration = clampDuration(duration);
1234        animationCount++;
1235
1236        Color ac = scc.filter(a.getColor());
1237        if(delay > 0)
1238            a.addAction(Actions.sequence(
1239                    Actions.delay(delay),
1240                    Actions.color(color, duration * 0.3f),
1241                    Actions.color(ac, duration * 0.7f),
1242                    Actions.delay(0.0f, Actions.run(new Runnable() {
1243                        @Override
1244                        public void run() {
1245                            recallActor(a);
1246                        }
1247                    }))));
1248        else
1249            a.addAction(Actions.sequence(
1250                    Actions.color(color, duration * 0.3f),
1251                    Actions.color(ac, duration * 0.7f),
1252                    Actions.delay(0.0f, Actions.run(new Runnable() {
1253                        @Override
1254                        public void run() {
1255                            recallActor(a);
1256                        }
1257                    }))));
1258        /*
1259        Actions.run(new Runnable() {
1260                        @Override
1261                        public void run() {
1262                            recallActor(a);
1263                        }
1264                    })
1265         */
1266                /*Actions.run(new Runnable() {
1267                    @Override
1268                    public void run() {
1269                        recallActor(a);
1270                    }
1271                })*/
1272    }
1273
1274    /**
1275         * Starts a tint animation for the object at {@code (x,y)} for the given
1276         * {@code duration} (in seconds).
1277         */
1278    public final void tint(int x, int y, Color color, float duration) {
1279        tint(0f, x, y, color, duration);
1280    }
1281
1282        /**
1283         * Fade the cell at {@code (x,y)} to {@code color}. Contrary to
1284         * {@link #tint(int, int, Color, float)}, this action does not restore the
1285         * cell's color at the end of its execution. This is for example useful to
1286         * fade the game screen when the rogue dies.
1287         * 
1288         * @param x
1289         * @param y
1290         * @param color
1291         *            The color at the end of the fadeout.
1292         * @param duration
1293         *            The fadeout's duration.
1294         */
1295        public void fade(int x, int y, Color color, float duration) {
1296                final Actor a = cellToActor(x, y);
1297                if (a == null)
1298                        return;
1299        duration = clampDuration(duration);
1300                animationCount++;
1301                final Color c = scc.filter(color);
1302                a.addAction(Actions.sequence(Actions.color(c, duration), Actions.run(new Runnable() {
1303                        @Override
1304                        public void run() {
1305                                recallActor(a);
1306                        }
1307                })));
1308        }
1309
1310        @Override
1311    public boolean hasActiveAnimations() {
1312        return animationCount != 0;
1313    }
1314
1315    public LinkedHashSet<AnimatedEntity> getAnimatedEntities() {
1316        return animatedEntities;
1317    }
1318
1319    public void removeAnimatedEntity(AnimatedEntity ae)
1320    {
1321        animatedEntities.remove(ae);
1322    }
1323
1324        @Override
1325        public ISquidPanel<Color> getBacker() {
1326                return this;
1327        }
1328
1329        /**
1330         * @return The current color center. Never {@code null}.
1331         */
1332        public IColorCenter<Color> getColorCenter() {
1333                return scc;
1334        }
1335
1336        /**
1337         * Use this method if you use your own {@link IColorCenter} and want this
1338         * panel not to allocate its own colors (or fill
1339         * {@link DefaultResources#getSCC()} but instead to the provided center.
1340         * 
1341         * @param scc
1342         *            The color center to use. Should not be {@code null}.
1343         * @return {@code this}
1344         * @throws NullPointerException
1345         *             If {@code scc} is {@code null}.
1346         */
1347        @Override
1348        public SquidPanel setColorCenter(IColorCenter<Color> scc) {
1349                if (scc == null)
1350                        /* Better fail now than later */
1351                        throw new NullPointerException(
1352                                        "The color center should not be null in " + getClass().getSimpleName());
1353                this.scc = scc;
1354                return this;
1355        }
1356
1357    public String getAt(int x, int y)
1358    {
1359        if(contents[x][y] == null)
1360            return "";
1361        return contents[x][y];
1362    }
1363    public Color getColorAt(int x, int y)
1364    {
1365        return colors[x][y];
1366    }
1367
1368    public Color getLightingColor() {
1369        return lightingColor;
1370    }
1371
1372    public void setLightingColor(Color lightingColor) {
1373        this.lightingColor = lightingColor;
1374    }
1375
1376    protected float clampDuration(float duration) {
1377        if (duration < 0.02f)
1378                return 0.02f;
1379        else
1380                return duration;
1381    }
1382
1383    /**
1384     * The X offset that the whole panel's internals will be rendered at.
1385     * @return the current offset in cells along the x axis
1386     */
1387    public int getGridOffsetX() {
1388        return gridOffsetX;
1389    }
1390
1391    /**
1392     * Sets the X offset that the whole panel's internals will be rendered at.
1393     * @param gridOffsetX the requested offset in cells; should be less than gridWidth
1394     */
1395    public void setGridOffsetX(int gridOffsetX) {
1396        if(gridOffsetX < gridWidth)
1397            this.gridOffsetX = gridOffsetX;
1398    }
1399
1400    /**
1401     * The Y offset that the whole panel's internals will be rendered at.
1402     * @return the current offset in cells along the y axis
1403     */
1404    public int getGridOffsetY() {
1405        return gridOffsetY;
1406    }
1407
1408    /**
1409     * Sets the Y offset that the whole panel's internals will be rendered at.
1410     * @param gridOffsetY the requested offset in cells; should be less than gridHeight
1411     */
1412    public void setGridOffsetY(int gridOffsetY) {
1413        if(gridOffsetY < gridHeight)
1414            this.gridOffsetY = gridOffsetY;
1415
1416    }
1417
1418    /**
1419     * The number of cells along the x-axis that will be rendered of this panel.
1420     * @return the number of cells along the x-axis that will be rendered of this panel
1421     */
1422    public int getGridWidth() {
1423        return gridWidth;
1424    }
1425
1426    /**
1427     * Sets the number of cells along the x-axis that will be rendered of this panel to gridWidth.
1428     * @param gridWidth the requested width in cells
1429     */
1430    public void setGridWidth(int gridWidth) {
1431        this.gridWidth = gridWidth;
1432    }
1433
1434    /**
1435     * The number of cells along the y-axis that will be rendered of this panel
1436     * @return the number of cells along the y-axis that will be rendered of this panel
1437     */
1438    public int getGridHeight() {
1439        return gridHeight;
1440    }
1441
1442    /**
1443     * Sets the number of cells along the y-axis that will be rendered of this panel to gridHeight.
1444     * @param gridHeight the requested height in cells
1445     */
1446    public void setGridHeight(int gridHeight) {
1447        this.gridHeight = gridHeight;
1448    }
1449
1450    /**
1451     * Sets the position of the actor's bottom left corner.
1452     *
1453     * @param x
1454     * @param y
1455     */
1456    @Override
1457    public void setPosition(float x, float y) {
1458        super.setPosition(x, y);
1459        setBounds(x, y, getWidth(), getHeight());
1460    }
1461
1462    public float getxOffset() {
1463        return xOffset;
1464    }
1465
1466    public void setOffsetX(float xOffset) {
1467        this.xOffset = xOffset;
1468    }
1469
1470    public float getyOffset() {
1471        return yOffset;
1472    }
1473
1474    public void setOffsetY(float yOffset) {
1475        this.yOffset = yOffset;
1476    }
1477
1478    public void setOffsets(float x, float y) {
1479        xOffset = x;
1480        yOffset = y;
1481    }
1482}