package squidpony.squidgrid.gui.gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.StretchViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import squidpony.squidmath.StatefulRNG;
/**
* A convenience class that groups several commonly-used GUI classes into one object and provides ways to
* initialize these kits for specific purposes, some of which would be a challenge to write without this code.
* Created by Tommy Ettinger on 8/11/2016.
*/
public class StarterKit {
/**
* One of the more critical parts of rendering text is what font to use, and textFactory should usually
* not be reassigned during a game because so many things depend on this value or a copy of it (so the
* change might not affect what it was expected to, and might break other things).
*/
public TextCellFactory textFactory;
/**
* The main way to interact with a text-based grid as for roguelikes. A SquidLayers object stores a
* background and foreground SquidPanel, and this configures them as requested.
*/
public SquidLayers layers;
/**
* The number of grid spaces on the x axis.
*/
public int gridWidth;
/**
* The number of grid spaces on the y axis.
*/
public int gridHeight;
/**
* The width of a cell that holds one char, in "relative pixels," where the screen is expected to stretch so
* one relative pixel does not generally refer to one actual screen pixel (since high-DPI phones and
* laptops may make a single pixel virtually impossible to see with the naked eye).
* <br>
* By default, this value is doubled to make stretching look more smooth.
*/
public int cellWidth;
/**
* The height of a cell that holds one char, in "relative pixels," where the screen is expected to stretch
* so one relative pixel does not generally refer to one actual screen pixel (since high-DPI phones and
* laptops may make a single pixel virtually impossible to see with the naked eye).
* <br>
* By default, this value is doubled to make stretching look more smooth.
*/
public int cellHeight;
/**
* All visible parts of this class are in the Stage, and if you add additional widget or scene2d.ui Actor
* values to your game, they should probably be added to this Stage.
*/
public Stage stage;
/**
* Used to draw lots of things, but mostly handled internally by the Stage.
* You may need to call {@code batch.begin()} and {@code batch.end()} in some cases where you want to
* render something that isn't a child of stage but is an Actor or similar render-able object.
*/
public SpriteBatch batch;
/**
* An important part of how this will be displayed; the viewport defaults to a displayed width of
* {@code cellWidth * gridWidth} and a displayed height of {@code cellHeight * gridHeight}, after cellWidth
* and cellHeight were doubled by default, and will be stretched or shrunk to fit the actual screen size.
*/
public Viewport viewport;
/**
* Almost all of SquidLib comes into contact with randomness at some point, so this is a good place to show one
* way of handling that randomness. StatefulRNG can be "seeded" at the start to set the initial state, like any
* other RNG, but it can also have the current state acquired later with {@link StatefulRNG#getState()} or have the
* current state set in-place with {@link StatefulRNG#setState(long)} (note, this doesn't create a new RNG, like you
* would have to do to re-seed with java.util.Random). This can be useful to get a snapshot of the random sequence
* where you might want to take an action, undo it back to the snapshot, and try again. It can also be useful for
* saving the game and reloading it exactly, though the optional serialization in squidlib-extra also does this.
* You can pass a StatefulRNG to anything that expects an RNG, and you'll encounter a lot of methods that employ
* RNG (and some that specifically require or prefer StatefulRNG) throughout squidlib-util.
* <br>
* This field defaults to a StatefulRNG seeded with the number SQUIDLIB (written in base 36), or 2252637788195L in
* base 10. Like most StatefulRNG instances, it uses a LightRNG internally, which is very fast and has a good-enough
* period (how many numbers it generates before repeating the cycle) at (2 to the 64) random numbers.
*/
public StatefulRNG rng = new StatefulRNG(2252637788195L);;
/**
* Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
* and height for each letter (cellWidth and cellHeight), using a default font that is about half as wide as it is
* tall but can stretch to other aspect ratios.
* @param gridWidth the width of the display area in cells
* @param gridHeight the height of the display area in cells
* @param cellWidth the width of a single cell in pixels, before any stretching is applied
* @param cellHeight the height of a single cell in pixels, before any stretching is applied
*/
public StarterKit(int gridWidth, int gridHeight, int cellWidth, int cellHeight)
{
this(DefaultResources.getStretchableFont(), gridWidth, gridHeight, cellWidth, cellHeight);
}
/**
* Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
* and height for each letter (cellWidth and cellHeight), using the given TextCellFactory for the font. You can use
* any of the pre-constructed TextCellFactory objects in {@link DefaultResources}. such as
* {@link DefaultResources#getStretchableTypewriterFont()}, {@link DefaultResources#getStretchableDejaVuFont()},
* {@link DefaultResources#getStretchableSquareFont()}, or {@link DefaultResources#getStretchableCodeFont()}, as
* long as you have the right assets available (their documentation says the exact files you need). While you can
* construct your own TextCellFactory given a BitmapFont, that won't work well as a distance field font unless you
* used some very unusual configuration making the font, so the font would only look good at one size or possibly a
* multiple of that size. The defaults are recommended for now; a separate project is used to make the distance
* field monospace fonts (tommyettinger/Glamer on GitHub) and more can be made as needed from permissively-licensed
* fonts if a game has particular aesthetic requirements.
* @param textFactory the TextCellFactory to use for the font
* @param gridWidth the width of the display area in cells
* @param gridHeight the height of the display area in cells
* @param cellWidth the width of a single cell in pixels, before any stretching is applied
* @param cellHeight the height of a single cell in pixels, before any stretching is applied
*/
public StarterKit(TextCellFactory textFactory, int gridWidth, int gridHeight, int cellWidth, int cellHeight) {
this(textFactory, gridWidth, gridHeight, cellWidth, cellHeight, 0, 0);
}
/**
* Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
* and height for each letter (cellWidth and cellHeight), using the given TextCellFactory for the font. You can use
* any of the pre-constructed TextCellFactory objects in {@link DefaultResources}. such as
* {@link DefaultResources#getStretchableTypewriterFont()}, {@link DefaultResources#getStretchableDejaVuFont()},
* {@link DefaultResources#getStretchableSquareFont()}, or {@link DefaultResources#getStretchableCodeFont()}, as
* long as you have the right assets available (their documentation says the exact files you need). While you can
* construct your own TextCellFactory given a BitmapFont, that won't work well as a distance field font unless you
* used some very unusual configuration making the font, so the font would only look good at one size or possibly a
* multiple of that size. The defaults are recommended for now; a separate project is used to make the distance
* field monospace fonts (tommyettinger/Glamer on GitHub) and more can be made as needed from permissively-licensed
* fonts if a game has particular aesthetic requirements.
* @param textFactory the TextCellFactory to use for the font
* @param gridWidth the width of the display area in cells
* @param gridHeight the height of the display area in cells
* @param cellWidth the width of a single cell in pixels, before any stretching is applied
* @param cellHeight the height of a single cell in pixels, before any stretching is applied
* @param additionalWidth the width in pixels to add to the stretched area, before any stretching is applied
* @param additionalHeight the height in pixels to add to the stretched area, before any stretching is applied
*/
public StarterKit(TextCellFactory textFactory, int gridWidth, int gridHeight, int cellWidth, int cellHeight,
int additionalWidth, int additionalHeight) {
this.cellWidth = cellWidth;
this.cellHeight = cellHeight;
this.textFactory = DefaultResources.getStretchableFont()
.width(this.cellWidth).height(this.cellHeight).initBySize();
layers = new SquidLayers(gridWidth, gridHeight, this.cellWidth, this.cellHeight, textFactory);
layers.setTextSize(cellWidth, cellHeight + 2);
this.gridWidth = gridWidth;
this.gridHeight = gridHeight;
batch = new SpriteBatch();
viewport = new StretchViewport(this.cellWidth * gridWidth + additionalWidth, this.cellHeight * gridHeight + additionalHeight);
stage = new Stage(viewport, batch);
stage.addActor(layers);
}
/**
* Not a complete drawing solution; so much of the logic related to drawing is specific to each game, like
* FOV being used to make certain things not render if they are out of sight, that this doesn't even try to
* guess at what a particular game needs for its rendering code. You should probably draw any AnimatedEntity
* objects, like what {@link SquidLayers#animateActor(int, int, char, Color)} returns, separately and after
* calling this method. The recommended way to draw those objects is with {@link #drawEntity(AnimatedEntity)},
* which must be called between SpriteBatch's begin() and end() methods, while this method cannot be called
* between those SpriteBatch methods. The solution, usually, is to call this method, then call
* {@link SpriteBatch#begin()}, do any logic to determine what AnimatedEntity objects need to be shown (are
* they in FOV, are they alive, etc.), draw the ones that should be shown with drawEntity(), and finally
* call {@link SpriteBatch#end()} when no more AnimatedEntity objects need to be drawn. Note that this
* method renders all of {@link #stage}, which may include other GUI elements using scene2d.ui, but the
* AnimatedEntity objects in a SquidLayers aren't part of any Stage to allow them to be rendered as special
* cases for visibility.
* <br>
* Specifically, this applies the current viewport to the stage, draws the stage, and makes any actions or
* events related to the stage take effect. Should not be called inside a {@link SpriteBatch#begin()} block,
* since this calls it itself by drawing the stage, and also calls {@link SpriteBatch#end()} afterwards.
*/
public void draw()
{
stage.getViewport().apply(true);
stage.draw();
stage.act();
}
/**
* Draws an AnimatedEntity object; must be called between {@link SpriteBatch#begin()} and {@link SpriteBatch#end()}.
* You can obtain the correct batch with the {@link #batch} field, and ideally all calls to this method will be
* inside a single block of one begin() and one end(), that is, the batch shouldn't start and end for each entity
* to draw.
* @param entity an AnimatedEntity to draw, usually obtained through one of many methods in {@link SquidLayers}
*/
public void drawEntity(AnimatedEntity entity)
{
if(batch.isDrawing())
layers.drawActor(batch, 1.0f, entity);
}
/**
* Not a complete resize method; this is meant to handle the resizing of this StarterKit only and should be
* called inside your main Game, ApplicationListener, etc. class' resize method.
* @param width the new width of the screen; should be a parameter from the other resize() method
* @param height the new height of the screen; should be a parameter from the other resize() method
*/
public void resize(int width, int height) {
viewport.update(width, height, true);
}
}