package squidpony.squidgrid.gui.gdx;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.NumberUtils;
import squidpony.ArrayTools;
import squidpony.IColorCenter;
import squidpony.StringKit;
import squidpony.panel.IColoredString;
import squidpony.panel.ISquidPanel;
import squidpony.squidgrid.Direction;
import squidpony.squidmath.Coord;
import squidpony.squidmath.OrderedSet;
import squidpony.squidmath.StatefulRNG;
import java.util.ArrayList;
import java.util.Collection;
import static com.badlogic.gdx.math.MathUtils.clamp;
/**
* Displays text and images in a grid pattern. Supports basic animations.
*
* Grid width and height settings are in terms of number of cells. Cell width and height
* are in terms of number of pixels.
*
* When text is placed, the background color is set separately from the foreground character. When moved, only the
* foreground character is moved.
*
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
*/
public class SquidPanel extends Group implements ISquidPanel<Color> {
public float DEFAULT_ANIMATION_DURATION = 0.12F;
protected int animationCount = 0;
protected Color defaultForeground = Color.WHITE;
protected IColorCenter<Color> scc;
protected final int cellWidth, cellHeight;
protected int gridWidth, gridHeight, gridOffsetX = 0, gridOffsetY = 0;
protected final char[][] contents;
protected final float[][] colors;
protected Color lightingColor = SColor.WHITE, tmpColor = new Color();
protected final TextCellFactory textFactory;
protected float xOffset, yOffset, lightingFloat = NumberUtils.intToFloatColor(0xffffffff);
public final OrderedSet<AnimatedEntity> animatedEntities;
public final OrderedSet<Actor> autoActors;
/**
* For thin-wall maps, where only cells where x and y are both even numbers have backgrounds displayed.
* Should be false when using this SquidPanel for anything that isn't specifically a background of a map
* that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the foregrounds of
* thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with DungeonUtility's
* hashesToLines method) makes thin lines for walls that should be displayed as between the boundaries of
* other cells. The overlap behavior needed for some "thin enough" cells to be displayed between the cells
* can be accomplished by using {@link #setTextSize(float, float)} to double the previously-given cell width
* and height.
*/
public boolean onlyRenderEven = false;
/**
* Creates a bare-bones panel with all default values for text rendering.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
*/
public SquidPanel(int gridWidth, int gridHeight) {
this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont());
}
/**
* Creates a panel with the given grid and cell size. Uses a default square font.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
* @param cellWidth the number of horizontal pixels in each cell
* @param cellHeight the number of vertical pixels in each cell
*/
public SquidPanel(int gridWidth, int gridHeight, int cellWidth, int cellHeight) {
this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont().width(cellWidth).height(cellHeight).initBySize());
}
/**
* Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
* are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
*
* If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
* then a default one will be created and initialized.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
* @param factory the factory to use for cell rendering
*/
public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory) {
this(gridWidth, gridHeight, factory, DefaultResources.getSCC());
}
/**
* Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
* are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
*
* If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
* then a default one will be created and initialized.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
* @param factory the factory to use for cell rendering
* @param center
* The color center to use. Can be {@code null}, but then must be set later on with
* {@link #setColorCenter(IColorCenter)}.
*/
public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center) {
this(gridWidth, gridHeight, factory, center, 0f, 0f);
}
/**
* Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
* are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
*
* If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
* then a default one will be created and initialized.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
* @param factory the factory to use for cell rendering
* @param center
* The color center to use. Can be {@code null}, but then must be set later on with
* {@link #setColorCenter(IColorCenter)}.
*/
public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center,
float xOffset, float yOffset) {
this(gridWidth, gridHeight, factory, center, xOffset, yOffset, null);
}
/**
* Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images
* are being used, a TextCellFactory is still needed to perform sizing and other utility functions.
*
* If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null
* then a default one will be created and initialized.
*
* @param gridWidth the number of cells horizontally
* @param gridHeight the number of cells vertically
* @param factory the factory to use for cell rendering
* @param center
* The color center to use. Can be {@code null}, but then must be set later on with
* {@link #setColorCenter(IColorCenter)}.
*/
public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center,
float xOffset, float yOffset, char[][] actualMap) {
this.gridWidth = gridWidth;
this.gridHeight = gridHeight;
if(center == null)
scc = DefaultResources.getSCC();
else
scc = center;
if (factory == null) {
textFactory = new TextCellFactory();
}
else
textFactory = factory;
if (!textFactory.initialized()) {
textFactory.initByFont();
}
cellWidth = MathUtils.round(textFactory.actualCellWidth);
cellHeight = MathUtils.round(textFactory.actualCellHeight);
if(actualMap == null || actualMap.length <= 0)
contents = ArrayTools.fill(' ', gridWidth, gridHeight);
else
contents = actualMap;
colors = ArrayTools.fill(scc.filter(Color.CLEAR).toFloatBits(), contents.length, contents[0].length);
int w = gridWidth * cellWidth;
int h = gridHeight * cellHeight;
this.xOffset = xOffset;
this.yOffset = yOffset;
setSize(w, h);
animatedEntities = new OrderedSet<>();
autoActors = new OrderedSet<>();
}
/**
* Places the given characters into the grid starting at 0,0.
*
* @param chars
*/
public void put(char[][] chars) {
put(0, 0, chars);
}
@Override
public void put(/* @Nullable */char[][] chars, Color[][] foregrounds) {
if (chars == null) {
/* Only colors to put */
final int width = foregrounds.length;
final int height = width == 0 ? 0 : foregrounds[0].length;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++)
put(x, y, foregrounds[x][y]);
}
} else
put(0, 0, chars, foregrounds);
}
public void put(char[][] chars, int[][] indices, ArrayList<Color> palette) {
put(0, 0, chars, indices, palette);
}
public void put(int xOffset, int yOffset, char[][] chars) {
put(xOffset, yOffset, chars, defaultForeground);
}
public void put(int xOffset, int yOffset, char[][] chars, Color[][] foregrounds) {
for (int x = xOffset; x < xOffset + chars.length; x++) {
for (int y = yOffset; y < yOffset + chars[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, chars[x - xOffset][y - yOffset], foregrounds[x - xOffset][y - yOffset]);
}
}
}
}
public void put(int xOffset, int yOffset, char[][] chars, int[][] indices, ArrayList<Color> palette) {
for (int x = xOffset; x < xOffset + chars.length; x++) {
for (int y = yOffset; y < yOffset + chars[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, chars[x - xOffset][y - yOffset], palette.get(indices[x - xOffset][y - yOffset]));
}
}
}
}
public void put(int xOffset, int yOffset, Color[][] foregrounds) {
for (int x = xOffset; x < xOffset + foregrounds.length; x++) {
for (int y = yOffset; y < yOffset + foregrounds[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, '\0', foregrounds[x - xOffset][y - yOffset]);
}
}
}
}
public void put(int xOffset, int yOffset, int[][] indices, ArrayList<Color> palette) {
for (int x = xOffset; x < xOffset + indices.length; x++) {
for (int y = yOffset; y < yOffset + indices[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, '\0', palette.get(indices[x - xOffset][y - yOffset]));
}
}
}
}
public void put(int xOffset, int yOffset, char[][] chars, Color foreground) {
for (int x = xOffset; x < xOffset + chars.length; x++) {
for (int y = yOffset; y < yOffset + chars[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, chars[x - xOffset][y - yOffset], foreground);
}
}
}
}
/**
* Puts the given string horizontally with the first character at the given offset.
*
* Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
* the grid size) will not be shown but will not cause any malfunctions.
*
* Will use the default color for this component to draw the characters.
*
* @param xOffset the x coordinate of the first character
* @param yOffset the y coordinate of the first character
* @param string the characters to be displayed
*/
public void put(int xOffset, int yOffset, String string) {
put(xOffset, yOffset, string, defaultForeground);
}
@Override
public void put(int xOffset, int yOffset, IColoredString<? extends Color> cs) {
int x = xOffset;
for (IColoredString.Bucket<? extends Color> fragment : cs) {
final String s = fragment.getText();
final Color color = fragment.getColor();
put(x, yOffset, s, color == null ? getDefaultForegroundColor() : scc.filter(color));
x += s.length();
}
}
@Override
public void put(int xOffset, int yOffset, String string, Color foreground) {
if(string == null || string.isEmpty())
return;
if (string.length() == 1) {
put(xOffset, yOffset, string.charAt(0), foreground);
}
else
{
float enc = scc.filter(foreground).toFloatBits();
for (int i = 0; i < string.length(); i++) {
put(xOffset + i, yOffset, string.charAt(i), enc);
}
}
}
public void put(int xOffset, int yOffset, String string, Color foreground, float colorMultiplier) {
if (string.length() == 1) {
put(xOffset, yOffset, string.charAt(0), foreground, colorMultiplier);
}
else
{
for (int i = 0; i < string.length(); i++) {
put(xOffset + i, yOffset, string.charAt(i), foreground, colorMultiplier);
}
}
}
public void put(int xOffset, int yOffset, char[][] chars, Color foreground, float colorMultiplier) {
for (int x = xOffset; x < xOffset + chars.length; x++) {
for (int y = yOffset; y < yOffset + chars[0].length; y++) {
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input
put(x, y, chars[x - xOffset][y - yOffset], foreground, colorMultiplier);
}
}
}
}
/**
* Puts the given string horizontally or optionally vertically, with the first character at the given offset.
*
* Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
* the grid size) will not be shown but will not cause any malfunctions.
*
* Will use the default color for this component to draw the characters.
*
* @param xOffset the x coordinate of the first character
* @param yOffset the y coordinate of the first character
* @param string the characters to be displayed
* @param vertical true if the text should be written vertically, from top to bottom
*/
public void put(int xOffset, int yOffset, String string, boolean vertical) {
put(xOffset, yOffset, string, defaultForeground, vertical);
}
/**
* Puts the given string horizontally or optionally vertically, with the first character at the given offset.
*
* Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
* the grid size) will not be shown but will not cause any malfunctions.
*
* @param xOffset the x coordinate of the first character
* @param yOffset the y coordinate of the first character
* @param string the characters to be displayed
* @param foreground the color to draw the characters
* @param vertical true if the text should be written vertically, from top to bottom
*/
public void put(int xOffset, int yOffset, String string, Color foreground, boolean vertical) {
if (vertical) {
for (int i = 0; i < string.length(); i++) {
put(xOffset, yOffset + i, string.charAt(i), foreground);
}
} else {
put(xOffset, yOffset, string, foreground);
}
}
/**
* Puts the given string in the chosen direction, with the first character shown (not necessarily the first in the
* string) at the given offset. If you use {@link Direction#LEFT}, then this effectively reverses the String and
* prints it with the last character of the String at the minimum-x position, which is the same position that the
* first character would be if you printed normally or if you gave this RIGHT (x is xOffset, y is yOffset). Giving
* UP acts similarly to LEFT, but has the last character at the minimum-y position and has the first character below
* it. The diagonals act as you would expect, combining the behavior of one of UP or DOWN with one of LEFT or RIGHT.
* <br>
* Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than
* the grid size) will not be shown but will not cause any malfunctions.
*
* @param xOffset the x coordinate of the first character
* @param yOffset the y coordinate of the first character
* @param string the characters to be displayed
* @param foreground the color to draw the characters
* @param direction the direction the text should be written in, such as {@link Direction#RIGHT} for normal layout
*/
public void put(int xOffset, int yOffset, String string, Color foreground, Direction direction) {
float enc = scc.filter(foreground).toFloatBits();
switch (direction)
{
case DOWN:
for (int i = 0; i < string.length(); i++) {
put(xOffset, yOffset + i, string.charAt(i), enc);
}
break;
case UP:
for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) {
put(xOffset, yOffset + p, string.charAt(i), enc);
}
break;
case LEFT:
for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) {
put(xOffset + p, yOffset, string.charAt(i), enc);
}
break;
case DOWN_RIGHT:
for (int i = 0; i < string.length(); i++) {
put(xOffset + i, yOffset + i, string.charAt(i), enc);
}
break;
case UP_RIGHT:
for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) {
put(xOffset + i, yOffset + p, string.charAt(i), enc);
}
break;
case UP_LEFT:
for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) {
put(xOffset + p, yOffset + p, string.charAt(i), enc);
}
break;
case DOWN_LEFT:
for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) {
put(xOffset + p, yOffset + i, string.charAt(i), enc);
}
break;
default:
for (int i = 0; i < string.length(); i++) {
put(xOffset + i, yOffset, string.charAt(i), enc);
}
}
}
/**
* Erases the entire panel, leaving only a transparent space.
*/
public void erase() {
ArrayTools.fill(contents, ' ');
ArrayTools.fill(colors, 0f);
}
@Override
public void clear(int x, int y) {
put(x, y, 0f);
}
@Override
public void put(int x, int y, Color color) {
put(x, y, '\0', color);
}
public void put(int x, int y, float encodedColor) {
put(x, y, '\0', encodedColor);
}
public void put(int x, int y, float encodedColor, float colorMultiplier) {
put(x, y, '\0', encodedColor, colorMultiplier);
}
public void put(int x, int y, float encodedColor, float colorMultiplier, float mixColor) {
put(x, y, '\0', encodedColor, colorMultiplier, mixColor);
}
public void put(int x, int y, Color color, float colorMultiplier) {
put(x, y, '\0', color, colorMultiplier);
}
public void put(int x, int y, Color color, float mixAmount, Color mix) {
put(x, y, '\0', color, mixAmount, mix);
}
@Override
public void put(int x, int y, char c) {
put(x, y, c, defaultForeground);
}
/**
* Takes a unicode codepoint for input.
*
* @param x
* @param y
* @param code
*/
public void put(int x, int y, int code) {
put(x, y, code, defaultForeground);
}
public void put(int x, int y, int c, Color color) {
put(x, y, String.valueOf(Character.toChars(c)), color);
}
public void put(int x, int y, int index, ArrayList<Color> palette) {
put(x, y, palette.get(index));
}
public void put(int x, int y, char c, int index, ArrayList<Color> palette) {
put(x, y, c, palette.get(index));
}
/**
* Takes a unicode char for input.
*
* @param x
* @param y
* @param c
* @param color
*/
@Override
public void put(int x, int y, char c, Color color) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = scc.filter(color).toFloatBits();
}
/**
* Takes a unicode char for input.
*
* @param x
* @param y
* @param c
* @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)}
*/
public void put(int x, int y, char c, float encodedColor) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = encodedColor;
}
/**
* Takes a unicode char for input and a color multiplier that determines how much of {@link #lightingColor} will
* affect the given encodedColor. The encodedColor is a float that might be produced by {@link Color#toFloatBits()}
* or by mixing multiple such floats with {@link SColor#lerpFloatColors(float, float, float)}.
*
* @param x
* @param y
* @param c
* @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)}
* @param colorMultiplier how much of {@link #lightingColor} to use in place of encodedColor, from 0.0 to 1.0
*/
public void put(int x, int y, char c, float encodedColor, float colorMultiplier) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = SColor.lerpFloatColors(encodedColor, lightingFloat, colorMultiplier);
}
/**
* Intended for colored lighting; takes a unicode char for input and a color multiplier that determines how much of
* mixColor will affect encodedColor. Both encodedColor and mixColor are floats that might be produced by
* {@link Color#toFloatBits()} or by mixing multiple such floats with
* {@link SColor#lerpFloatColors(float, float, float)}; colorMultiplier is a normal float between 0.0f and 1.0f .
*
* @param x
* @param y
* @param c
* @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)}
* @param colorMultiplier how much of mixColor to use in place of encodedColor, from 0.0 to 1.0
* @param mixColor a color to mix with encodedColor, typically as part of colored lighting
*/
public void put(int x, int y, char c, float encodedColor, float colorMultiplier, float mixColor) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = SColor.lerpFloatColors(encodedColor, mixColor, colorMultiplier);
}
/**
* Puts the given character at position x, y, with its color determined by the given color interpolated with
* this SquidPanel's lightingColor (default is white light) by the amount specified by colorMultiplier (0.0
* causes no change to the given color, 1.0 uses the lightingColor only, and anything between 0 and 1 will
* produce some tint to color, and probably cache the produced color in the IColorCenter this uses).
*/
public void put(int x, int y, char c, Color color, float colorMultiplier) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = scc.lerp(color, lightingColor, colorMultiplier).toFloatBits();
}
/**
* Puts the given character at position x, y, with its color determined by the given color interpolated with
* the given mix color by the amount specified by mixAmount (0.0 causes no change to the given color, 1.0 uses mix
* only, and anything between 0 and 1 will produce some tint to color, and probably cache the produced color in the
* IColorCenter this uses).
* <br>
* Note, unlike {@link #put(int, int, char, float, float, float)}, this will use the IColorCenter to produce the
* finished color, which may be slightly slower if you don't need any of IColorCenter's features, and will use
* more memory if many colors are cached, but has the advantage of being able to adjust colors with filters.
*/
public void put(int x, int y, char c, Color color, float mixAmount, Color mix) {
if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) {
return;//skip if out of bounds
}
contents[x][y] = c;
colors[x][y] = scc.lerp(color, mix, mixAmount).toFloatBits();
}
@Override
public int cellWidth() {
return cellWidth;
}
@Override
public int cellHeight() {
return cellHeight;
}
@Override
public int gridHeight() {
return gridHeight;
}
@Override
public int gridWidth() {
return gridWidth;
}
/**
* @return The {@link TextCellFactory} backing {@code this}.
*/
public TextCellFactory getTextCellFactory() {
return textFactory;
}
/**
* Sets the size of the text in this SquidPanel (but not the size of the cells) to the given width and height in
* pixels (which may be stretched by viewports later on, if your program uses them).
* @param wide the width of a glyph in pixels
* @param high the height of a glyph in pixels
* @return this for chaining
*/
public SquidPanel setTextSize(float wide, float high)
{
textFactory.tweakHeight(high).tweakWidth(wide).initBySize();
//textFactory.setSmoothingMultiplier((3f + Math.max(cellWidth * 1f / wide, cellHeight * 1f / high)) / 4f);
return this;
}
@Override
public void draw (Batch batch, float parentAlpha) {
textFactory.configureShader(batch);
int inc = onlyRenderEven ? 2 : 1, widthInc = inc * cellWidth, heightInc = inc * cellHeight;
float screenX = xOffset - (gridOffsetX <= 0 ? 0 : cellWidth)/* - getX()*/,
screenY_base = 1f + yOffset + (gridOffsetY <= 0 ? 0 : cellHeight) + gridHeight * cellHeight, screenY;
for (int x = Math.max(0, gridOffsetX-1), xx = (gridOffsetX <= 0) ? 0 : -1; xx <= gridWidth && x < contents.length; x += inc, xx += inc, screenX += widthInc) {
screenY = screenY_base;
for (int y = Math.max(0, gridOffsetY-1), yy = (gridOffsetY <= 0) ? 0 : -1; yy <= gridHeight && y < contents[x].length; y += inc, yy += inc, screenY -= heightInc) {
textFactory.draw(batch, contents[x][y],
colors[x][y],
screenX,// xOffset + /*- getX() + 1f * */ x * cellWidth,
screenY // yOffset + /*- getY() + 1f * */ (gridHeight - y) * cellHeight + 1f
);
}
}
super.draw(batch, parentAlpha);
int len = animatedEntities.size();
for (int i = 0; i < len; i++) {
animatedEntities.getAt(i).actor.act(Gdx.graphics.getDeltaTime());
}
len = autoActors.size();
Actor a;
for (int i = 0; i < len; i++) {
a = autoActors.getAt(i);
if(a == null) continue;
drawActor(batch, parentAlpha, a);
a.act(Gdx.graphics.getDeltaTime());
}
}
/**
* Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end()
* @param batch Must have start() called already but not stop() yet during this frame.
* @param parentAlpha This can be assumed to be 1.0f if you don't know it
* @param ae The AnimatedEntity to draw; the position to draw ae is stored inside it.
*/
public void drawActor(Batch batch, float parentAlpha, AnimatedEntity ae)
{
drawActor(batch, parentAlpha, ae.actor);
/*
float prevX = ae.actor.getX(), prevY = ae.actor.getY();
ae.actor.setPosition(prevX - gridOffsetX * cellWidth, prevY + gridOffsetY * cellHeight);
ae.actor.draw(batch, parentAlpha);
ae.actor.setPosition(prevX, prevY);
*/
}
/**
* Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end()
* @param batch Must have start() called already but not stop() yet during this frame.
* @param parentAlpha This can be assumed to be 1.0f if you don't know it
* @param ac The Actor to draw; the position to draw ac is modified and reset based on some fields of this object
*/
public void drawActor(Batch batch, float parentAlpha, Actor ac)
{
float prevX = ac.getX(), prevY = ac.getY();
ac.setPosition(prevX - gridOffsetX * cellWidth, prevY + gridOffsetY * cellHeight);
ac.draw(batch, parentAlpha);
ac.setPosition(prevX, prevY);
}
@Override
public void setDefaultForeground(Color defaultForeground) {
this.defaultForeground = defaultForeground;
}
@Override
public Color getDefaultForegroundColor() {
return defaultForeground;
}
public AnimatedEntity getAnimatedEntityByCell(int x, int y) {
for(AnimatedEntity ae : animatedEntities)
{
if(ae.gridX == x && ae.gridY == y)
return ae;
}
return null;
}
/**
* Create an AnimatedEntity at position x, y, using the char c in the given color.
* @param x
* @param y
* @param c
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, char c, Color color)
{
return animateActor(x, y, false, c, color);
/*
Actor a = textFactory.makeActor("" + c, color);
a.setName("" + c);
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
AnimatedEntity ae = new AnimatedEntity(a, x, y);
animatedEntities.add(ae);
return ae;
*/
}
/**
* Create an AnimatedEntity at position x, y, using the char c in the given color. If doubleWidth is true, treats
* the char c as the left char to be placed in a grid of 2-char cells.
* @param x
* @param y
* @param doubleWidth
* @param c
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Color color)
{
return animateActor(x, y, doubleWidth, c, color);
/*
Actor a = textFactory.makeActor("" + c, color);
a.setName("" + c);
if(doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
*/
}
/**
* Create an AnimatedEntity at position x, y, using the String s in the given color.
* @param x
* @param y
* @param s
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, String s, Color color)
{
return animateActor(x, y, false, s, color);
}
/**
* Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
* the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
* @param x
* @param y
* @param doubleWidth
* @param s
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Color color)
{
Actor a = textFactory.makeActor(s, color);
a.setName(s);
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
* the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
* @param x
* @param y
* @param doubleWidth
* @param s
* @param colors
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors)
{
return animateActor(x, y, doubleWidth, s, colors, 2f);
}
/**
* Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats
* the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells.
* @param x
* @param y
* @param doubleWidth
* @param s
* @param colors
* @param loopTime
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors, float loopTime)
{
Actor a = textFactory.makeActor(s, colors, loopTime, doubleWidth);
a.setName(s);
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using '^' as its contents, but as an image so it can be rotated.
* Uses the given colors in a looping pattern, that doesn't count as an animation. If doubleWidth is true, treats
* the '^' as starting in the middle of a 2-char cell.
* @param x
* @param y
* @param doubleWidth
* @param colors
* @param loopTime
* @return
*/
public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Collection<Color> colors, float loopTime)
{
Actor a = textFactory.makeDirectionMarker(colors, loopTime, doubleWidth);
a.setName("^");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Color color)
{
Actor a = textFactory.makeDirectionMarker(color);
a.setName("^");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using the char c with a color looked up by index in palette.
* @param x
* @param y
* @param c
* @param index
* @param palette
* @return
*/
public AnimatedEntity animateActor(int x, int y, char c, int index, ArrayList<Color> palette)
{
return animateActor(x, y, c, palette.get(index));
}
/**
* Create an AnimatedEntity at position x, y, using the String s with a color looked up by index in palette.
* @param x
* @param y
* @param s
* @param index
* @param palette
* @return
*/
public AnimatedEntity animateActor(int x, int y, String s, int index, ArrayList<Color> palette)
{
return animateActor(x, y, s, palette.get(index));
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be
* stretched to fit one cell.
* @param x
* @param y
* @param texture
* @return
*/
public AnimatedEntity animateActor(int x, int y, TextureRegion texture)
{
return animateActor(x, y, false, texture, Color.WHITE);
/*
Actor a = textFactory.makeActor(texture, Color.WHITE);
a.setName("");
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
AnimatedEntity ae = new AnimatedEntity(a, x, y);
animatedEntities.add(ae);
return ae;
*/
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
* stretched to fit one cell.
* @param x
* @param y
* @param texture
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, TextureRegion texture, Color color)
{
return animateActor(x, y, false, texture, color);
/*
Actor a = textFactory.makeActor(texture, color);
a.setName("");
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
AnimatedEntity ae = new AnimatedEntity(a, x, y);
animatedEntities.add(ae);
return ae;
*/
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be
* stretched to fit one cell, or two cells if doubleWidth is true.
* @param x
* @param y
* @param doubleWidth
* @param texture
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture)
{
return animateActor(x, y, doubleWidth, texture, Color.WHITE);
/*
Actor a = textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
a.setName("");
if(doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
*/
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
* stretched to fit one cell, or two cells if doubleWidth is true.
* @param x
* @param y
* @param doubleWidth
* @param texture
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Color color) {
Actor a = textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
a.setName("");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
/*
if (doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
*/
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
* stretched to fit one cell, or two cells if doubleWidth is true.
* @param x
* @param y
* @param doubleWidth
* @param texture
* @param colors
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors){
return animateActor(x, y, doubleWidth, texture, colors, 2f);
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be
* stretched to fit one cell, or two cells if doubleWidth is true.
* @param x
* @param y
* @param doubleWidth
* @param texture
* @param colors
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors, float loopTime) {
Actor a = textFactory.makeActor(texture, colors, loopTime, doubleWidth, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
a.setName("");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
/*
if (doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
*/
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which, if and only
* if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false,
* this will preserve the existing size of texture.
* @param x
* @param y
* @param doubleWidth
* @param stretch
* @param texture
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture)
{
Actor a = (stretch)
? textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight)
: textFactory.makeActor(texture, Color.WHITE, texture.getRegionWidth(), texture.getRegionHeight());
a.setName("");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
/*
if(doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
*/
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which, if and only
* if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false,
* this will preserve the existing size of texture.
* @param x
* @param y
* @param doubleWidth
* @param stretch
* @param texture
* @param color
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture, Color color) {
Actor a = (stretch)
? textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight)
: textFactory.makeActor(texture, color, texture.getRegionWidth(), texture.getRegionHeight());
a.setName("");
a.setPosition(adjustX(x, doubleWidth), adjustY(y));
/*
if (doubleWidth)
a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
else
a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY());
*/
AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth);
animatedEntities.add(ae);
return ae;
}
/**
* Created an Actor from the contents of the given x,y position on the grid.
* @param x
* @param y
* @return
*/
public Actor cellToActor(int x, int y)
{
return cellToActor(x, y, false);
}
/**
* Created an Actor from the contents of the given x,y position on the grid; deleting
* the grid's String content at this cell.
*
* @param x
* @param y
* @param doubleWidth
* @return A fresh {@link Actor}, that has just been added to {@code this}.
*/
public Actor cellToActor(int x, int y, boolean doubleWidth)
{
return createActor(x, y, contents[x][y], colors[x][y], doubleWidth);
}
protected /* @Nullable */ Actor createActor(int x, int y, char name, Color color, boolean doubleWidth) {
final Actor a = textFactory.makeActor(name, scc.filter(color));
a.setName(String.valueOf(name));
a.setPosition(adjustX(x, doubleWidth) - getX(), adjustY(y) - getY());
autoActors.add(a);
return a;
}
protected /* @Nullable */ Actor createActor(int x, int y, char name, float encodedColor, boolean doubleWidth) {
final Actor a = textFactory.makeActor(name, encodedColor);
a.setName(String.valueOf(name));
a.setPosition(adjustX(x, doubleWidth) - getX(), adjustY(y) - getY());
autoActors.add(a);
return a;
}
public float adjustX(float x, boolean doubleWidth)
{
if(doubleWidth)
return (x - gridOffsetX) * 2 * cellWidth + getX();
else
return (x) * cellWidth + getX();
}
public float adjustY(float y)
{
return (gridHeight - y - 1) * cellHeight + getY() + 1 + cellHeight - textFactory.actualCellHeight; // - textFactory.lineHeight //textFactory.lineTweak * 3f
//return (gridHeight - y - 1) * cellHeight + textFactory.getDescent() * 3 / 2f + getY();
}
/*
public void startAnimation(Actor a, int oldX, int oldY)
{
Coord tmp = Coord.get(oldX, oldY);
tmp.x = Math.round(a.getX() / cellWidth);
tmp.y = gridHeight - Math.round(a.getY() / cellHeight) - 1;
if(tmp.x >= 0 && tmp.x < gridWidth && tmp.y > 0 && tmp.y < gridHeight)
{
}
}
*/
public void recallActor(Actor a, boolean restoreSym)
{
animationCount--;
int x = Math.round((a.getX() - getX()) / cellWidth) + gridOffsetX,
y = gridHeight - (int)(a.getY() / cellHeight) - 1 + gridOffsetY;
if(onlyRenderEven)
{
// this just sets the least significant bit to 0, making any odd numbers even (decrementing)
x &= -2;
y &= -2;
}
if(x < 0 || y < 0 || x >= contents.length || y >= contents[x].length)
return;
String n;
if (restoreSym && (n = a.getName()) != null && !n.isEmpty())
contents[x][y] = a.getName().charAt(0);
removeActor(a);
autoActors.remove(a);
}
public void recallActor(Actor a, boolean restoreSym, int nextX, int nextY)
{
animationCount--;
if(onlyRenderEven)
{
// this just sets the least significant bit to 0, making any odd numbers even (decrementing)
nextX &= -2;
nextY &= -2;
}
if(nextX < 0 || nextY < 0 || nextX >= contents.length || nextY >= contents[nextX].length)
return;
String n;
if (restoreSym && (n = a.getName()) != null && !n.isEmpty())
contents[nextX][nextY] = a.getName().charAt(0);
removeActor(a);
autoActors.remove(a);
}
public void recallActor(AnimatedEntity ae)
{
if(ae.doubleWidth)
ae.gridX = Math.round((ae.actor.getX() - getX()) / (2 * cellWidth)) + gridOffsetX;
else
ae.gridX = Math.round((ae.actor.getX() - getX()) / cellWidth) + gridOffsetY;
ae.gridY = gridHeight - (int)((ae.actor.getY() - getY()) / cellHeight) - 1 + gridOffsetY;
if(onlyRenderEven)
{
// this just sets the least significant bit to 0, making any odd numbers even (decrementing)
ae.gridX &= -2;
ae.gridY &= -2;
}
ae.animating = false;
animationCount--;
}
public void recallActor(AnimatedEntity ae, int nextX, int nextY)
{
ae.gridX = nextX;
ae.gridY = nextY;
if(onlyRenderEven)
{
// this just sets the least significant bit to 0, making any odd numbers even (decrementing)
ae.gridX &= -2;
ae.gridY &= -2;
}
//fixPosition(ae);
ae.animating = false;
animationCount--;
}
public void fixPosition(AnimatedEntity ae)
{
ae.actor.setPosition(adjustX(ae.gridX, ae.doubleWidth), adjustY(ae.gridY));
}
public void fixPositions()
{
for (int i = 0; i < animatedEntities.size(); i++) {
AnimatedEntity ae = animatedEntities.getAt(i);
ae.actor.setPosition(adjustX(ae.gridX, ae.doubleWidth), adjustY(ae.gridY));
}
}
/**
* Start a bumping animation in the given direction that will last duration seconds.
* @param ae an AnimatedEntity returned by animateActor()
* @param direction
* @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f
*/
public void bump(final AnimatedEntity ae, Direction direction, float duration)
{
final Actor a = ae.actor;
final float x = adjustX(ae.gridX, ae.doubleWidth),
y = adjustY(ae.gridY);
if(a == null || ae.animating) return;
duration = clampDuration(duration);
animationCount++;
ae.animating = true;
a.addAction(Actions.sequence(
Actions.moveToAligned(x + (direction.deltaX / 3F) * ((ae.doubleWidth) ? 2F : 1F), y + direction.deltaY / 3F,
Align.center, duration * 0.35F),
Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.65F),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(ae, ae.gridX, ae.gridY);
}
}))));
}
/**
* Start a bumping animation in the given direction that will last duration seconds.
* @param x
* @param y
* @param direction
* @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f
*/
public void bump(final int x, final int y, Direction direction, float duration)
{
final Actor a = cellToActor(x, y);
if(a == null) return;
duration = clampDuration(duration);
animationCount++;
float nextX = adjustX(x, false), nextY = adjustY(y);
/*
x *= cellWidth;
y = (gridHeight - y - 1);
y *= cellHeight;
y -= 1;
x += getX();
y += getY();
*/
a.addAction(Actions.sequence(
Actions.moveToAligned(nextX + direction.deltaX / 3F, nextY + direction.deltaY / 3F,
Align.center, duration * 0.35F),
Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.65F),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, true, x, y);
}
}))));
}
/**
* Starts a bumping animation in the direction provided.
*
* @param x
* @param y
* @param direction
*/
public void bump(int x, int y, Direction direction) {
bump(x, y, direction, DEFAULT_ANIMATION_DURATION);
}
/**
* Starts a bumping animation in the direction provided.
*
* @param location
* @param direction
*/
public void bump(Coord location, Direction direction) {
bump(location.x, location.y, direction, DEFAULT_ANIMATION_DURATION);
}
/**
* Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of
* seconds given by duration (often 0.12f or somewhere around there).
* @param ae an AnimatedEntity returned by animateActor()
* @param newX
* @param newY
* @param duration
*/
public void slide(final AnimatedEntity ae, final int newX, final int newY, float duration)
{
final Actor a = ae.actor;
final float nextX = adjustX(newX, ae.doubleWidth), nextY = adjustY(newY);
if(a == null || ae.animating) return;
duration = clampDuration(duration);
animationCount++;
ae.animating = true;
ae.gridX = newX;
ae.gridY = newY;
a.addAction(Actions.sequence(
Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(ae, newX, newY);
}
}))));
}
/**
* Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of
* seconds given by duration (often 0.12f or somewhere around there).
* @param x
* @param y
* @param newX
* @param newY
* @param duration
*/
public void slide(int x, int y, final int newX, final int newY, float duration)
{
final Actor a = cellToActor(x, y);
if(a == null) return;
duration = clampDuration(duration);
animationCount++;
float nextX = adjustX(newX, false), nextY = adjustY(newY);
/*
newX *= cellWidth;
newY = (gridHeight - newY - 1);
newY *= cellHeight;
newY -= 1;
x += getX();
y += getY();
*/
a.addAction(Actions.sequence(
Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, true, newX, newY);
}
}))));
}
/**
* Slides {@code name} from {@code (x,y)} to {@code (newx, newy)}. If
* {@code name} or {@code
* color} is {@code null}, it is picked from this panel (hereby removing the
* current name, if any).
*
* @param x
* Where to start the slide, horizontally.
* @param y
* Where to start the slide, vertically.
* @param name
* The name to slide, or {@code null} to pick it from this
* panel's {@code (x,y)} cell.
* @param color
* The color to use, or {@code null} to pick it from this panel's
* {@code (x,y)} cell.
* @param newX
* Where to end the slide, horizontally.
* @param newY
* Where to end the slide, vertically.
* @param duration
* The animation's duration.
*/
public void slide(int x, int y, final /* @Nullable */ String name, /* @Nullable */ Color color, int newX,
int newY, float duration) {
slide(x, y, name, color, newX, newY, duration, null);
}
/**
* Slides {@code name} from {@code (x,y)} to {@code (newx, newy)}. If
* {@code name} or {@code color} is {@code null}, it is picked from this
* panel (thereby removing the current name, if any). This also allows
* a Runnable to be given as {@code postRunnable} to be run after the
* slide completes.
*
* @param x
* Where to start the slide, horizontally.
* @param y
* Where to start the slide, vertically.
* @param name
* The name to slide, or {@code null} to pick it from this
* panel's {@code (x,y)} cell.
* @param color
* The color to use, or {@code null} to pick it from this panel's
* {@code (x,y)} cell.
* @param newX
* Where to end the slide, horizontally.
* @param newY
* Where to end the slide, vertically.
* @param duration
* The animation's duration.
* @param postRunnable a Runnable to execute after the slide completes; may be null to do nothing.
*/
public void slide(int x, int y, final /* @Nullable */ String name, /* @Nullable */ Color color, final int newX,
final int newY, float duration, /* @Nullable */ Runnable postRunnable) {
if(name != null && name.isEmpty())
return;
final Actor a = createActor(x, y, name == null ? contents[x][y] : name.charAt(0),
color == null ? SColor.colorFromFloat(tmpColor, colors[x][y]) : color, false);
if (a == null)
return;
duration = clampDuration(duration);
animationCount++;
final int nbActions = 2 + (postRunnable == null ? 0 : 1);
int index = 0;
final Action[] sequence = new Action[nbActions];
final float nextX = adjustX(newX, false);
final float nextY = adjustY(newY);
sequence[index++] = Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration);
if(postRunnable != null)
{
sequence[index++] = Actions.run(postRunnable);
}
/* Do this one last, so that hasActiveAnimations() returns true during 'postRunnables' */
sequence[index] = Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, name == null, newX, newY);
}
}));
a.addAction(Actions.sequence(sequence));
}
/**
* Starts a movement animation for the object at the given grid location at the default speed.
*
* @param start Coord to pick up a tile from and slide
* @param end Coord to end the slide on
*/
public void slide(Coord start, Coord end) {
slide(start.x, start.y, end.x, end.y, DEFAULT_ANIMATION_DURATION);
}
/**
* Starts a movement animation for the object at the given grid location at the default speed for one grid square in
* the direction provided.
*
* @param start Coord to pick up a tile from and slide
* @param direction Direction enum that indicates which way the slide should go
*/
public void slide(Coord start, Direction direction) {
slide(start.x, start.y, start.x + direction.deltaX, start.y + direction.deltaY, DEFAULT_ANIMATION_DURATION);
}
/**
* Starts a sliding movement animation for the object at the given location at the provided speed. The duration is
* how many seconds should pass for the entire animation.
*
* @param start
* @param end
* @param duration
*/
public void slide(Coord start, Coord end, float duration) {
slide(start.x, start.y, end.x, end.y, duration);
}
/**
* Starts an wiggling animation for the object at the given location for the given duration in seconds.
*
* @param ae an AnimatedEntity returned by animateActor()
* @param duration
*/
public void wiggle(final AnimatedEntity ae, float duration) {
final Actor a = ae.actor;
final float x = adjustX(ae.gridX, ae.doubleWidth), y = adjustY(ae.gridY);
//final int x = ae.gridX * cellWidth * ((ae.doubleWidth) ? 2 : 1) + (int)getX(), y = (gridHeight - ae.gridY - 1) * cellHeight - 1 + (int)getY();
if(a == null || ae.animating)
return;
duration = clampDuration(duration);
ae.animating = true;
animationCount++;
StatefulRNG gRandom = DefaultResources.getGuiRandom();
a.addAction(Actions.sequence(
Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.2F),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(ae, ae.gridX, ae.gridY);
}
}))));
}
/**
* Starts an wiggling animation for the object at the given location for the given duration in seconds.
*
* @param x
* @param y
* @param duration
*/
public void wiggle(final int x, final int y, float duration) {
final Actor a = cellToActor(x, y);
if(a == null) return;
duration = clampDuration(duration);
animationCount++;
float nextX = adjustX(x, false), nextY = adjustY(y);
/*
x *= cellWidth;
y = (gridHeight - y - 1);
y *= cellHeight;
y -= 1;
x += getX();
y += getY();
*/
StatefulRNG gRandom = DefaultResources.getGuiRandom();
a.addAction(Actions.sequence(
Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f,
nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f,
Align.bottomLeft, duration * 0.2F),
Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.2F),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, true, x, y);
}
}))));
}
/**
* Starts a tint animation for {@code ae} for the given {@code duration} in seconds.
*
* @param ae an AnimatedEntity returned by animateActor()
* @param color what to transition ae's color towards, and then transition back from
* @param duration how long the total "round-trip" transition should take in seconds
*/
public void tint(final AnimatedEntity ae, Color color, float duration) {
final Actor a = ae.actor;
if(a == null)
return;
duration = clampDuration(duration);
ae.animating = true;
animationCount++;
Color ac = scc.filter(a.getColor());
a.addAction(Actions.sequence(
Actions.color(color, duration * 0.3f),
Actions.color(ac, duration * 0.7f),
Actions.delay(duration, Actions.run(new Runnable() {
@Override
public void run() {
recallActor(ae, ae.gridX, ae.gridY);
}
}))));
}
/**
* Like {@link #tint(int, int, Color, float)}, but waits for {@code delay}
* (in seconds) before performing it.
* @param delay how long to wait in seconds before starting the effect
* @param x the x-coordinate of the cell to tint
* @param y the y-coordinate of the cell to tint
* @param color what to transition ae's color towards, and then transition back from
* @param duration how long the total "round-trip" transition should take in seconds
*/
public void tint(float delay, int x, int y, Color color, float duration) {
tint(delay, x, y, color, duration, null);
}
/**
* Like {@link #tint(int, int, Color, float)}, but waits for {@code delay}
* (in seconds) before performing it. Additionally, enqueue {@code postRunnable}
* for running after the created action ends.
* @param delay how long to wait in seconds before starting the effect
* @param x the x-coordinate of the cell to tint
* @param y the y-coordinate of the cell to tint
* @param color what to transition ae's color towards, and then transition back from
* @param duration how long the total "round-trip" transition should take in seconds
* @param postRunnable a Runnable to execute after the tint completes; may be null to do nothing.
*/
public void tint(float delay, final int x, final int y, Color color, float duration, Runnable postRunnable) {
final Actor a = cellToActor(x, y);
if(a == null)
return;
duration = clampDuration(duration);
animationCount++;
Color ac = scc.filter(a.getColor());
final int nbActions = 3 + (0 < delay ? 1 : 0) + (postRunnable == null ? 0 : 1);
final Action[] sequence = new Action[nbActions];
int index = 0;
if (0 < delay)
sequence[index++] = Actions.delay(delay);
sequence[index++] = Actions.color(color, duration * 0.3f);
sequence[index++] = Actions.color(ac, duration * 0.7f);
if(postRunnable != null)
{
sequence[index++] = Actions.run(postRunnable);
}
/* Do this one last, so that hasActiveAnimations() returns true during 'postRunnable' */
sequence[index] = Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, true, x, y);
}
});
a.addAction(Actions.sequence(sequence));
}
/**
* Starts a tint animation for the object at {@code (x,y)} for the given
* {@code duration} (in seconds).
* @param x the x-coordinate of the cell to tint
* @param y the y-coordinate of the cell to tint
* @param color
* @param duration
*/
public void tint(int x, int y, Color color, float duration) {
tint(0f, x, y, color, duration);
}
/**
* Fade the cell at {@code (x,y)} to {@code color}. Contrary to
* {@link #tint(int, int, Color, float)}, this action does not restore the
* cell's color at the end of its execution. This is for example useful to
* fade the game screen when the rogue dies.
*
* @param x the x-coordinate of the cell to tint
* @param y the y-coordinate of the cell to tint
* @param color
* The color at the end of the fadeout.
* @param duration
* The fadeout's duration.
*/
public void fade(final int x, final int y, Color color, float duration) {
final Actor a = cellToActor(x, y);
if (a == null)
return;
duration = clampDuration(duration);
animationCount++;
final Color c = scc.filter(color);
a.addAction(Actions.sequence(Actions.color(c, duration), Actions.run(new Runnable() {
@Override
public void run() {
recallActor(a, true, x, y);
}
})));
}
/**
* Create a new Actor at (x, y) that looks like glyph but can rotate, and immediately starts changing color from
* startColor to endColor and changing rotation from startRotation to endRotation, taking duration seconds to
* complete before removing the Actor.
* @param x the x position in cells; doesn't change
* @param y the y position in cells; doesn't change
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param startColor the starting Color
* @param endColor the Color to transition to
* @param startRotation the amount of rotation, in degrees, the glyph should start at
* @param endRotation the amount of rotation, in degrees, the glyph should end at
* @param duration the duration in seconds for the effect
*/
public void summon(int x, int y, char glyph, Color startColor, Color endColor,
float startRotation, float endRotation, float duration)
{
summon(x, y, x, y, glyph, startColor, endColor, false, startRotation, endRotation, duration);
}
/**
* Create a new Actor at (startX, startY) that looks like glyph but can rotate, sets its color, and immediately
* starts changing position so it ends on the cell (endX, endY) and changing rotation from startRotation to
* endRotation, taking duration seconds to complete before removing the Actor.
* @param startX the starting x position in cells
* @param startY the starting y position in cells
* @param endX the ending x position in cells
* @param endY the ending y position in cells
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param color the Color of the glyph throughout the effect
* @param startRotation the amount of rotation, in degrees, the glyph should start at
* @param endRotation the amount of rotation, in degrees, the glyph should end at
* @param duration the duration in seconds for the effect
*/
public void summon(int startX, int startY, int endX, int endY, char glyph, Color color,
float startRotation, float endRotation, float duration)
{
summon(startX, startY, endX, endY, glyph, color, color, false, startRotation, endRotation, duration);
}
/**
* Create a new Actor at (startX, startY) that looks like glyph but has the given rotation, and immediately starts
* changing color from startColor to endColor, and changing position so it ends on the cell (endX, endY), taking
* duration seconds to complete before removing the Actor.
* @param startX the starting x position in cells
* @param startY the starting y position in cells
* @param endX the ending x position in cells
* @param endY the ending y position in cells
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param startColor the starting Color
* @param endColor the Color to transition to
* @param rotation the amount of rotation, in degrees, the glyph should have throughout the effect
* @param duration the duration in seconds for the effect
*/
public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor,
float rotation, float duration)
{
summon(startX, startY, endX, endY, glyph, startColor, endColor, false, rotation, rotation, duration);
}
/**
* Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing
* color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation
* from startRotation to endRotation, taking duration seconds to complete before removing the Actor.
* @param startX the starting x position in cells
* @param startY the starting y position in cells
* @param endX the ending x position in cells
* @param endY the ending y position in cells
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param startColor the starting Color
* @param endColor the Color to transition to
* @param startRotation the amount of rotation, in degrees, the glyph should start at
* @param endRotation the amount of rotation, in degrees, the glyph should end at
* @param duration the duration in seconds for the effect
*/
public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor,
float startRotation, float endRotation, float duration)
{
summon(startX, startY, endX, endY, glyph, startColor, endColor, false, startRotation, endRotation, duration);
}
/**
* Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing
* color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation
* from startRotation to endRotation, taking duration seconds to complete before removing the Actor. Allows
* setting doubleWidth, which centers the created Actor in the space between the two glyphs in a cell.
* @param startX the starting x position in cells
* @param startY the starting y position in cells
* @param endX the ending x position in cells
* @param endY the ending y position in cells
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param startColor the starting Color
* @param endColor the Color to transition to
* @param doubleWidth true if this uses double-width cells, false in most cases
* @param startRotation the amount of rotation, in degrees, the glyph should start at
* @param endRotation the amount of rotation, in degrees, the glyph should end at
* @param duration the duration in seconds for the effect
*/
public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor, boolean doubleWidth,
float startRotation, float endRotation, float duration)
{
summon(0f, startX, startY, endX, endY, glyph, startColor, endColor, doubleWidth, startRotation, endRotation, duration);
}
/**
* Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing
* color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation
* from startRotation to endRotation, taking duration seconds to complete before removing the Actor. Allows
* setting doubleWidth, which centers the created Actor in the space between the two glyphs in a cell.
* @param delay amount of time, in seconds, to wait before starting the effect
* @param startX the starting x position in cells
* @param startY the starting y position in cells
* @param endX the ending x position in cells
* @param endY the ending y position in cells
* @param glyph the char to show (the same char throughout the effect, but it can rotate)
* @param startColor the starting Color
* @param endColor the Color to transition to
* @param doubleWidth true if this uses double-width cells, false in most cases
* @param startRotation the amount of rotation, in degrees, the glyph should start at
* @param endRotation the amount of rotation, in degrees, the glyph should end at
* @param duration the duration in seconds for the effect
*/
public void summon(float delay, int startX, int startY, int endX, int endY, char glyph,
Color startColor, Color endColor, boolean doubleWidth,
float startRotation, float endRotation, float duration)
{
duration = clampDuration(duration);
animationCount++;
final ColorChangeImage
gi = textFactory.makeGlyphImage(glyph, scc.gradient(startColor, endColor, (int) (duration * 40)), duration * 1.1f, doubleWidth);
gi.setPosition(adjustX(startX, doubleWidth) - getX(), adjustY(startY) - getY());
gi.setRotation(startRotation);
autoActors.add(gi);
final int nbActions = 2 + (0 < delay ? 1 : 0);
final Action[] sequence = new Action[nbActions];
int index = 0;
if (0 < delay)
sequence[index++] = Actions.delay(delay);
sequence[index++] = Actions.parallel(
Actions.moveTo(adjustX(endX, doubleWidth) - getX(), adjustY(endY) - getY(), duration),
Actions.rotateTo(endRotation, duration));
sequence[index] = Actions.run(new Runnable() {
@Override
public void run() {
recallActor(gi, false);
}
});
gi.addAction(Actions.sequence(sequence));
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* This overload always moves Actors 1 cell away, which is a safe default, uses a "normal" amount of rotation for
* for all of the actors (a value of 1f if you used another overload), and always uses an end color that is a
* modified copy of startColor with 0 alpha (making the Actors all fade to transparent). The parameter
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions.
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, boolean eightWay, char glyph,
Color startColor,
float duration)
{
burst(0f, x, y, 1, eightWay, glyph, startColor, startColor.cpy().sub(0,0,0,1), false, 1f, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* This overload always moves Actors 1 cell away, which is a safe default, and uses a "normal" amount of rotation
* for all of the actors (a value of 1f if you used another overload). The parameter
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions.
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, boolean eightWay, char glyph,
Color startColor, Color endColor,
float duration)
{
burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, false, 1f, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* This overload always moves Actors 1 cell away, which is a safe default. Some parameters need explanation:
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions;
* rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to
* start at the correct rotation and not change that rotation over the course of the effect, but can be between 0
* and 1 or higher than 1 (negative values may also work).
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, boolean eightWay, char glyph,
Color startColor, Color endColor,
float rotationStrength, float duration)
{
burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, false, rotationStrength, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y);
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions;
* rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to
* start at the correct rotation and not change that rotation over the course of the effect, but can be between 0
* and 1 or higher than 1 (negative values may also work).
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square)
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, int distance, boolean eightWay, char glyph,
Color startColor, Color endColor,
float rotationStrength, float duration)
{
burst(0f, x, y, distance, eightWay, glyph, startColor, endColor, false, rotationStrength, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* This overload always moves Actors 1 cell away, which is a safe default. Some parameters need explanation:
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions;
* rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to
* start at the correct rotation and not change that rotation over the course of the effect, but can be between 0
* and 1 or higher than 1 (negative values may also work).
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param doubleWidth true if this should use the double-width-cell technique, false in most cases
* @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, boolean eightWay, char glyph,
Color startColor, Color endColor, boolean doubleWidth,
float rotationStrength, float duration)
{
burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, doubleWidth, rotationStrength, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y);
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions;
* rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to
* start at the correct rotation and not change that rotation over the course of the effect, but can be between 0
* and 1 or higher than 1 (negative values may also work).
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square)
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param doubleWidth true if this should use the double-width-cell technique, false in most cases
* @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation
* @param duration how long, in seconds, the effect should last
*/
public void burst(int x, int y, int distance, boolean eightWay, char glyph,
Color startColor, Color endColor, boolean doubleWidth,
float rotationStrength, float duration)
{
burst(0f, x, y, distance, eightWay, glyph, startColor, endColor, doubleWidth, rotationStrength, duration);
}
/**
* Convenience method to produce an explosion, splash, or burst effect. Calls
* {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with
* different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation.
* Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y);
* eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions;
* rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to
* start at the correct rotation and not change that rotation over the course of the effect, but can be between 0
* and 1 or higher than 1 (negative values may also work).
* @param delay amount of time, in seconds, to wait before starting the effect
* @param x the starting, center, x-position to create all Actors at
* @param y the starting, center, y-position to create all Actors at
* @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square)
* @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond
* @param glyph the char to make a rotate-able Actor of; should definitely be visible
* @param startColor the color to start the effect with
* @param endColor the color to end the effect on
* @param doubleWidth true if this should use the double-width-cell technique, false in most cases
* @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation
* @param duration how long, in seconds, the effect should last
*/
public void burst(float delay, int x, int y, int distance, boolean eightWay, char glyph,
Color startColor, Color endColor, boolean doubleWidth,
float rotationStrength, float duration)
{
Direction d;
if(eightWay)
{
for (int i = 0; i < 8; i++) {
d = Direction.CLOCKWISE[i];
summon(delay, x, y, x - d.deltaX * distance, y + d.deltaY * distance,
glyph, startColor, endColor, doubleWidth,
45f * i, 45f * (i - rotationStrength),
duration);
}
}
else
{
for (int i = 0; i < 4; i++) {
d = Direction.CARDINALS_CLOCKWISE[i];
summon(delay, x, y, x - d.deltaX * distance, y + d.deltaY * distance,
glyph, startColor, endColor, doubleWidth,
90f * i, 90f * (i - rotationStrength),
duration);
}
}
}
@Override
public boolean hasActiveAnimations() {
//return animationCount != 0;
if (0 < animationCount)
return true;
else
return 0 < getActions().size;
}
public OrderedSet<AnimatedEntity> getAnimatedEntities() {
return animatedEntities;
}
public void removeAnimatedEntity(AnimatedEntity ae)
{
animatedEntities.remove(ae);
}
@Override
public ISquidPanel<Color> getBacker() {
return this;
}
/**
* @return The current color center. Never {@code null}.
*/
public IColorCenter<Color> getColorCenter() {
return scc;
}
/**
* Use this method if you use your own {@link IColorCenter} and want this
* panel not to allocate its own colors (or fill
* {@link DefaultResources#getSCC()} but instead to the provided center.
*
* @param scc
* The color center to use. Should not be {@code null}.
* @return {@code this}
* @throws NullPointerException
* If {@code scc} is {@code null}.
*/
@Override
public SquidPanel setColorCenter(IColorCenter<Color> scc) {
if (scc == null)
/* Better fail now than later */
throw new NullPointerException(
"The color center should not be null in " + getClass().getSimpleName());
this.scc = scc;
return this;
}
public char getAt(int x, int y)
{
return contents[x][y];
}
public Color getColorAt(int x, int y)
{
return SColor.colorFromFloat(tmpColor, colors[x][y]);
}
public Color getLightingColor() {
return lightingColor;
}
public void setLightingColor(Color lightingColor) {
this.lightingColor = lightingColor;
lightingFloat = lightingColor.toFloatBits();
}
protected float clampDuration(float duration) {
if (duration < 0.02f)
return 0.02f;
else
return duration;
}
/**
* The X offset that the whole panel's internals will be rendered at. If the {@code gridWidth} of this SquidPanel is
* less than the actual size of the char[][] it renders, then you can use gridOffsetX to start rendering at a
* different position
* @return the current offset in cells along the x axis
*/
public int getGridOffsetX() {
return gridOffsetX;
}
/**
* Sets the X offset that the whole panel's internals will be rendered at.
* @param gridOffsetX the requested offset in cells
*/
public void setGridOffsetX(int gridOffsetX) {
this.gridOffsetX = clamp(gridOffsetX,0, contents.length - gridWidth);
}
/**
* The Y offset that the whole panel's internals will be rendered at.
* @return the current offset in cells along the y axis
*/
public int getGridOffsetY() {
return gridOffsetY;
}
/**
* Sets the Y offset that the whole panel's internals will be rendered at.
* @param gridOffsetY the requested offset in cells
*/
public void setGridOffsetY(int gridOffsetY) {
this.gridOffsetY = clamp(gridOffsetY,0, contents[0].length - gridHeight);
}
/**
* The number of cells along the x-axis that will be rendered of this panel.
* @return the number of cells along the x-axis that will be rendered of this panel
*/
public int getGridWidth() {
return gridWidth;
}
/**
* Sets the number of cells along the x-axis that will be rendered of this panel to gridWidth.
* @param gridWidth the requested width in cells
*/
public void setGridWidth(int gridWidth) {
this.gridWidth = gridWidth;
}
/**
* The number of cells along the y-axis that will be rendered of this panel
* @return the number of cells along the y-axis that will be rendered of this panel
*/
public int getGridHeight() {
return gridHeight;
}
/**
* Sets the number of cells along the y-axis that will be rendered of this panel to gridHeight.
* @param gridHeight the requested height in cells
*/
public void setGridHeight(int gridHeight) {
this.gridHeight = gridHeight;
}
/**
* Gets the total number of cells along the x-axis that this stores; this is usually equivalent to
* {@link #getGridWidth()}, but not if the constructor
* {@link #SquidPanel(int, int, TextCellFactory, IColorCenter, float, float, char[][])} was used to set a
* larger-than-normal map.
* @return the width of the internal array this can render, which may be larger than the visible width
*/
public int getTotalWidth()
{
return contents.length;
}
/**
* Gets the total number of cells along the y-axis that this stores; this is usually equivalent to
* {@link #getGridHeight()}, but not if the constructor
* {@link #SquidPanel(int, int, TextCellFactory, IColorCenter, float, float, char[][])} was used to set a
* larger-than-normal map.
* @return the height of the internal array this can render, which may be larger than the visible height
*/
public int getTotalHeight()
{
return contents[0].length;
}
/**
* Sets the position of the actor's bottom left corner.
*
* @param x
* @param y
*/
@Override
public void setPosition(float x, float y) {
super.setPosition(x, y);
setBounds(x, y, getWidth(), getHeight());
}
public float getxOffset() {
return xOffset;
}
public void setOffsetX(float xOffset) {
this.xOffset = xOffset;
}
public float getyOffset() {
return yOffset;
}
public void setOffsetY(float yOffset) {
this.yOffset = yOffset;
}
public void setOffsets(float x, float y) {
xOffset = x;
yOffset = y;
}
/**
* Gets the status of a boolean flag used for rendering thin maps; it will almost always be false unless it
* was set to true with {@link #setOnlyRenderEven(boolean)}.
* <br>
* This is meant for thin-wall maps, where only cells where x and y are both even numbers have backgrounds
* displayed. Should be false when using this SquidPanel for anything that isn't specifically a background
* of a map that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the
* foregrounds of thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with
* DungeonUtility's hashesToLines() method) makes thin lines for walls that should be displayed as between
* the boundaries of other cells. The overlap behavior needed for some "thin enough" cells to be displayed
* between the cells can be accomplished by using {@link #setTextSize(float, float)} to double the
* previously-given cell width and height.
*
* @return the current status of the onlyRenderEven flag, which defaults to false
*/
public boolean getOnlyRenderEven() {
return onlyRenderEven;
}
/**
* Sets the status of a boolean flag used for rendering thin maps; it should almost always be the default,
* which is false, unless you are using a thin-wall map, and then this should be true only if this
* SquidPanel is used for the background layer.
* <br>
* This is meant for thin-wall maps, where only cells where x and y are both even numbers have backgrounds
* displayed. Should be false when using this SquidPanel for anything that isn't specifically a background
* of a map that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the
* foregrounds of thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with
* DungeonUtility's hashesToLines() method) makes thin lines for walls that should be displayed as between
* the boundaries of other cells. The overlap behavior needed for some "thin enough" cells to be displayed
* between the cells can be accomplished by using {@link #setTextSize(float, float)} to double the
* previously-given cell width and height.
*
* @param onlyRenderEven generally, should only be true if this SquidPanel is a background of a thin map
*/
public void setOnlyRenderEven(boolean onlyRenderEven) {
this.onlyRenderEven = onlyRenderEven;
}
/**
* Gets a "snapshot" of the data represented by this SquidPanel; stores the dimensions, the char data, and the color
* data in a way that can be set back to a SquidPanel using {@link #setFromSnapshot(String, int, int, int, int)} or
* its overload that takes a StringBuilder. The actual contents of the returned StringBuilder are unlikely to be
* legible in any way if read as text, and are meant to be concise and stable across versions.
* @return a StringBuilder representation of this SquidPanel's data that can be passed later to {@link #setFromSnapshot(StringBuilder, int, int, int, int)} or converted to String and passed to its overload
*/
public StringBuilder getSnapshot()
{
return getSnapshot(0, 0, gridWidth, gridHeight);
}
/**
* Gets a "snapshot" of the data represented by this SquidPanel; stores the dimensions, the char data, and the color
* data in a way that can be set back to a SquidPanel using {@link #setFromSnapshot(String, int, int, int, int)} or
* its overload that takes a StringBuilder. The actual contents of the returned StringBuilder are unlikely to be
* legible in any way if read as text, and are meant to be concise and stable across versions. This overload allows
* the first x and y position used to be specified, as well as the width and height to use (the actual width and
* height stored may be different if this SquidPanel's gridWidth and/or gridHeight are smaller than the width and/or
* height given).
* @param startX the first x position to use in the snapshot, inclusive
* @param startY the first y position to use in the snapshot, inclusive
* @param width how wide the snapshot area should be; x positions from startX to startX + width - 1 will be used
* @param height how tall the snapshot area should be; y positions from startY to startY + height - 1 will be used
* @return a StringBuilder representation of this SquidPanel's data that can be passed later to {@link #setFromSnapshot(StringBuilder, int, int, int, int)} or converted to String and passed to its overload
*/
public StringBuilder getSnapshot(int startX, int startY, int width, int height) {
width = Math.min(gridWidth - startX, width);
height = Math.min(gridHeight - startY, height);
StringBuilder sb = new StringBuilder(width * height * 9 + 12);
sb.append(width).append('x').append(height).append(':');
for (int x = startX, i = 0; i < width; x++, i++) {
sb.append(contents[x], startY, height);
}
char[] reuse = new char[8];
for (int x = startX, i = 0; i < width; x++, i++) {
for (int y = startY, j = 0; j < height; y++, j++) {
sb.append(SColor.floatToChars(reuse, colors[x][y]));
}
}
return sb;
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data.
* @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(StringBuilder snapshot)
{
return setFromSnapshot(snapshot, 0, 0, -1, -1);
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot
* (considering putX and putY as offsets) so they have the values stored in the snapshot.
* @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @param putX where to start placing the data from the snapshot, x position
* @param putY where to start placing the data from the snapshot, y position
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY)
{
return setFromSnapshot(snapshot, putX, putY, -1, -1);
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) so
* they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full width
* and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this
* SquidPanel).
* @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @param putX where to start placing the data from the snapshot, x position
* @param putY where to start placing the data from the snapshot, y position
* @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed
* @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY, int limitWidth, int limitHeight)
{
if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this;
if(putX < 0) putX = 0;
if(putY < 0) putY = 0;
int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot),
height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start),
run = start;
if(limitWidth < 0)
limitWidth = Math.min(width, gridWidth - putX);
else
limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX));
if(limitHeight < 0)
limitHeight = Math.min(height, gridHeight - putY);
else
limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY));
for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) {
snapshot.getChars(run, run + limitHeight, contents[x], putY);
}
run = start + width * height;
for (int x = putX, i = 0; i < limitWidth; x++, i++) {
for (int y = putY, j = 0; j < limitHeight; y++, j++) {
colors[x][y] = SColor.charsToFloat(snapshot, run);
run += 8;
}
}
return this;
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data.
* <br>
* This overload takes a String instead of a StringBuilder for potentially-easier loading from files.
* @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(String snapshot)
{
return setFromSnapshot(snapshot, 0, 0, -1, -1);
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot
* (considering putX and putY as offsets) so they have the values stored in the snapshot.
* <br>
* This overload takes a String instead of a StringBuilder for potentially-easier loading from files.
* @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @param putX where to start placing the data from the snapshot, x position
* @param putY where to start placing the data from the snapshot, y position
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(String snapshot, int putX, int putY)
{
return setFromSnapshot(snapshot, putX, putY, -1, -1);
}
/**
* Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this
* SquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) so
* they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full width
* and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this
* SquidPanel).
* <br>
* This overload takes a String instead of a StringBuilder for potentially-easier loading from files.
* @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)}
* @param putX where to start placing the data from the snapshot, x position
* @param putY where to start placing the data from the snapshot, y position
* @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed
* @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed
* @return this after setting, for chaining
*/
public SquidPanel setFromSnapshot(String snapshot, int putX, int putY, int limitWidth, int limitHeight)
{
if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this;
if(putX < 0) putX = 0;
if(putY < 0) putY = 0;
int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot),
height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start),
run = start;
if(limitWidth < 0)
limitWidth = Math.min(width, gridWidth - putX);
else
limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX));
if(limitHeight < 0)
limitHeight = Math.min(height, gridHeight - putY);
else
limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY));
for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) {
snapshot.getChars(run, run + limitHeight, contents[x], putY);
}
run = start + width * height;
for (int x = putX, i = 0; i < limitWidth; x++, i++) {
for (int y = putY, j = 0; j < limitHeight; y++, j++) {
colors[x][y] = SColor.charsToFloat(snapshot, run);
run += 8;
}
}
return this;
}
}