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.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.IntMap;
import squidpony.IColorCenter;
import squidpony.StringKit;
import squidpony.panel.ISquidPanel;
import java.util.ArrayList;
import java.util.Collection;
/**
* Displays text and images in a grid pattern, like SquidPanel, but will automatically render certain chars as images.
* Supports basic animations, such as sliding, wiggling, or fading the contents of a cell.
* <br>
* Grid width and height settings are in terms of number of cells. Cell width and height are in terms of number of
* pixels (when there is no stretching taking place due to viewport or window size).
* <br>
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
* @author Tommy Ettinger
*/
public class ImageSquidPanel extends SquidPanel {
public final IntMap<TextureRegion> imageMap;
/**
* 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 ImageSquidPanel(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 ImageSquidPanel(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 ImageSquidPanel(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 ImageSquidPanel(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 ImageSquidPanel(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 ImageSquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center,
float xOffset, float yOffset, char[][] actualMap) {
super(gridWidth, gridHeight, factory, center, xOffset, yOffset, actualMap);
imageMap = new IntMap<>(128);
}
@Override
public void draw (Batch batch, float parentAlpha) {
textFactory.configureShader(batch);
int inc = onlyRenderEven ? 2 : 1, widthInc = inc * cellWidth, heightInc = inc * cellHeight;
TextureRegion tr;
float screenX = xOffset - (gridOffsetX <= 0 ? 0 : cellWidth)/* - getX()*/,
screenY_base = 1f + yOffset + (gridOffsetY <= 0 ? 0 : cellHeight) + gridHeight * cellHeight, screenY;
char c;
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) {
c = contents[x][y];
tr = imageMap.get(c);
if(tr == null)
textFactory.draw(batch, c, colors[x][y], screenX, screenY);
else
textFactory.draw(batch, tr, colors[x][y], screenX, screenY);
}
}
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);
}
/**
* 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);
}
/**
* 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)
{
TextureRegion tr = imageMap.get(c);
if(tr != null)
return animateActor(x, y, doubleWidth, tr, color, String.valueOf(c));
Actor a = textFactory.makeActor(c, color);
a.setName(String.valueOf(c));
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.
* @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 the char c in the given colors to cycle through. 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 c
* @param colors
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Collection<Color> colors)
{
return animateActor(x, y, doubleWidth, c, colors, 2f);
}
/**
* Create an AnimatedEntity at position x, y, using the char c in the given colors to cycle through. 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 c
* @param colors
* @param loopTime
* @return
*/
public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Collection<Color> colors, float loopTime)
{
TextureRegion tr = imageMap.get(c);
if(tr != null)
return animateActor(x, y, doubleWidth, tr, colors, loopTime, String.valueOf(c));
Actor a = textFactory.makeActor(c, colors, loopTime, doubleWidth);
a.setName(String.valueOf(c));
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){
return animateActor(x, y, doubleWidth, texture, color, "");
}
/**
* 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, String name){
Actor a = textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
a.setName(name);
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) {
return animateActor(x, y, doubleWidth, texture, colors, loopTime, "");
}
/**
* 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, String name) {
Actor a = textFactory.makeActor(texture, colors, loopTime, doubleWidth, (doubleWidth ? 2 : 1) * cellWidth, cellHeight);
a.setName(name);
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 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--;
}
@Override
public ISquidPanel<Color> getBacker() {
return this;
}
/**
* 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 ImageSquidPanel setColorCenter(IColorCenter<Color> scc) {
super.setColorCenter(scc);
return this;
}
/**
* Gets a "snapshot" of the data represented by this ImageSquidPanel; stores the dimensions, the char data, and the
* color data in a way that can be set back to a SquidPanel or ImageSquidPanel 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.
* <br>
* NOTE: For this version, the mapping of chars to images is not stored in the snapshot, allowing alternate mappings
* to be used, such as while graphics are being updated frequently. This also allows the snapshot to be read in from
* both normal SquidPanels and ImageSquidPanels.
* @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 ImageSquidPanel; stores the dimensions, the char data, and the
* color data in a way that can be set back to a SquidPanel or ImageSquidPanel 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).
* <br>
* NOTE: For this version, the mapping of chars to images is not stored in the snapshot, allowing alternate mappings
* to be used, such as while graphics are being updated frequently. This also allows the snapshot to be read in from
* both normal SquidPanels and ImageSquidPanels.
* @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
* ImageSquidPanel 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 ImageSquidPanel 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
* ImageSquidPanel 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 ImageSquidPanel 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
* ImageSquidPanel 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
* ImageSquidPanel).
* @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 ImageSquidPanel 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
* ImageSquidPanel 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 ImageSquidPanel 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
* ImageSquidPanel 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 ImageSquidPanel 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
* ImageSquidPanel 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
* ImageSquidPanel).
* <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 ImageSquidPanel 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;
}
/**
* Makes it so when the char swapOut would be drawn, the TextureRegion swapIn is drawn instead.
* This will apply for as long as this ImageSquidPanel is in use unless swapIn is removed with
* {@link #removeImageSwap(char)} or directly changing this object's {@link #imageMap}.
* @param swapOut the char to avoid rendering and replace with an image
* @param swapIn the image to replace the character with
*/
public void setImageSwap(final char swapOut, final TextureRegion swapIn)
{
if(swapIn != null)
imageMap.put(swapOut, swapIn);
}
/**
* Removes the char toRemove from the mapping of chars to replace with images, or does nothing if this did not
* already replace toRemove with an image. This means toRemove will render as itself, if present in the font.
* @param toRemove the char to render as a glyph instead of a texture
*/
public void removeImageSwap(final char toRemove)
{
imageMap.remove(toRemove);
}
/**
* If there is a TextureRegion that would replace the char toFind when drawn, this will return that TextureRegion,
* otherwise it returns null.
* @param toFind the char to find the corresponding TextureRegion that it could be mapped to
* @return the corresponding TextureRegion if it exists, or null otherwise
*/
public TextureRegion getImageSwap(final char toFind)
{
return imageMap.get(toFind);
}
/**
* Meant for taking easy-to-write chars and generating chars that can map to images, while still allowing the
* original easy-to-write char to be used as its own non-image char. Given a char that should be from the
* "relatively common" parts of Unicode (essentially, anything with a codepoint before 0x1000, or with a small risk
* of collision, anything before 0x8000), this returns a character from the "Private Use Area" of Unicode that will
* probably be unique for most kinds of input. 4096 possible chars can be returned by this method.
*
* As an example, you may want to map the char 'g' to an image of a goblin, but still sometimes show the actual 'g'
* for some other monster or a variant on goblins you don't have an image for. You could call
* {@code char goblinChar = ImageSquidPanel.getUnusedChar('g');} and then register goblinChar in the image mapping
* with {@code myImageSquidPanel.setImageSwap(goblinChar, goblinImage);}, which would allow you to draw 'g' directly
* by placing a 'g' with put(), or draw goblinImage by placing either goblinChar or
* {@code ImageSquidPanel.getUnusedChar('g')} with put().
*
* @param initial a char that ideally should be from the earlier parts of Unicode (before 0x1000 is ideal)
* @return a char from Unicode's private use area that is unlikely to be found otherwise or accidentally
*/
public static char getUnusedChar(final char initial)
{
return (char)(((initial & 0x7000) >>> 3 ^ initial) & 0x0FFF | 0xE000);
}
}