001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.graphics.g2d.Batch;
004import com.badlogic.gdx.scenes.scene2d.Stage;
005import com.badlogic.gdx.utils.viewport.StretchViewport;
006import com.badlogic.gdx.utils.viewport.Viewport;
007import squidpony.squidmath.GWTRNG;
008
009/**
010 * A convenience class that groups several commonly-used GUI classes into one object and provides ways to
011 * initialize these kits for specific purposes, some of which would be a challenge to write without this code.
012 * Created by Tommy Ettinger on 8/11/2016.
013 */
014public class StarterKit {
015    /**
016     * One of the more critical parts of rendering text is what font to use, and textFactory should usually
017     * not be reassigned during a game because so many things depend on this value or a copy of it (so the
018     * change might not affect what it was expected to, and might break other things).
019     */
020    public TextCellFactory textFactory;
021    /**
022     * The main way to interact with a text-based grid as for roguelikes. A SquidLayers object stores a
023     * background and foreground SquidPanel, and this configures them as requested.
024     */
025    public SparseLayers layers;
026    /**
027     * The number of grid spaces on the x axis.
028     */
029    public int gridWidth;
030    /**
031     * The number of grid spaces on the y axis.
032     */
033    public int gridHeight;
034    /**
035     * The width of a cell that holds one char, in "relative pixels," where the screen is expected to stretch so
036     * one relative pixel does not generally refer to one actual screen pixel (since high-DPI phones and
037     * laptops may make a single pixel virtually impossible to see with the naked eye).
038     * <br>
039     * By default, this value is doubled to make stretching look more smooth.
040     */
041    public int cellWidth;
042    /**
043     * The height of a cell that holds one char, in "relative pixels," where the screen is expected to stretch
044     * so one relative pixel does not generally refer to one actual screen pixel (since high-DPI phones and
045     * laptops may make a single pixel virtually impossible to see with the naked eye).
046     * <br>
047     * By default, this value is doubled to make stretching look more smooth.
048     */
049    public int cellHeight;
050
051    /**
052     * All visible parts of this class are in the Stage, and if you add additional widget or scene2d.ui Actor
053     * values to your game, they should probably be added to this Stage.
054     */
055    public Stage stage;
056    /**
057     * Used to draw lots of things, but mostly handled internally by the Stage.
058     * You may need to call {@code batch.begin()} and {@code batch.end()} in some cases where you want to
059     * render something that isn't a child of stage but is an Actor or similar render-able object.
060     * This can apply a filter from {@link FloatFilters} (or a custom {@link FloatFilter}) to all drawn colors. 
061     */
062    public FilterBatch batch;
063    /**
064     * An important part of how this will be displayed; the viewport defaults to a displayed width of
065     * {@code cellWidth * gridWidth} and a displayed height of {@code cellHeight * gridHeight}, after cellWidth
066     * and cellHeight were doubled by default, and will be stretched or shrunk to fit the actual screen size.
067     */
068    public Viewport viewport;
069
070    /**
071     * Almost all of SquidLib comes into contact with randomness at some point, so this is a good place to show one
072     * way of handling that randomness. GWTRNG acts as a normal implementation of {@link squidpony.squidmath.IRNG},
073     * can be "seeded" at the start to set the initial state, like any other RNG, but it can also have the current state
074     * acquired later with {@link GWTRNG#getState()} or have the current state set in-place with
075     * {@link GWTRNG#setState(long)} (note, this doesn't create a new RNG, like you would have to do to re-seed with
076     * java.util.Random). This can be useful to get a snapshot of the random sequence where you might want to take an
077     * action, undo it back to the snapshot, and try again. It can also be useful for saving the game and reloading it
078     * exactly, though the optional serialization in squidlib-extra also does this. You can pass a GWTRNG to anything
079     * that expects an IRNG, and you'll encounter a lot of methods that employ IRNG (and some that specifically require
080     * or prefer {@link squidpony.squidmath.IStatefulRNG}, which includes GWTRNG) throughout squidlib-util.
081     * <br>
082     * This field defaults to a GWTRNG seeded with the number SQUIDLIB (written in base 36), or 2252637788195L in
083     * base 10. Its algorithm can produce 2 to the 64 minus 1 numbers before repeating, and as the name might suggest,
084     * it should perform especially well on Google Web Toolkit for HTML deployment.
085     */
086    public GWTRNG rng = new GWTRNG(2252637788195L);
087
088    /**
089     * Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
090     * and height for each letter (cellWidth and cellHeight), using a default font that is about half as wide as it is
091     * tall but can stretch to other aspect ratios.
092     * @param gridWidth the width of the display area in cells
093     * @param gridHeight the height of the display area in cells
094     * @param cellWidth the width of a single cell in pixels, before any stretching is applied
095     * @param cellHeight the height of a single cell in pixels, before any stretching is applied
096     */
097    public StarterKit(int gridWidth, int gridHeight, int cellWidth, int cellHeight)
098    {
099        this(DefaultResources.getStretchableFont(), gridWidth, gridHeight, cellWidth, cellHeight);
100    }
101    /**
102     * Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
103     * and height for each letter (cellWidth and cellHeight), using the given TextCellFactory for the font. You can use
104     * any of the pre-constructed TextCellFactory objects in {@link DefaultResources}, such as
105     * {@link DefaultResources#getCrispLeanFamily()}, {@link DefaultResources#getCrispDejaVuFont()},
106     * {@link DefaultResources#getCrispSlabFamily()}, or {@link DefaultResources#getStretchableTypewriterFont()}, as
107     * long as you have the right assets available (their documentation says the exact files you need). While you can
108     * construct your own TextCellFactory given a BitmapFont, that won't work well as a distance field font unless you
109     * used some very unusual configuration making the font, so the font would only look good at one size or possibly a
110     * multiple of that size. The defaults are recommended for now; a separate project is used to make the distance
111     * field monospace fonts (<a href="https://github.com/tommyettinger/Glamer">tommyettinger/Glamer</a> on GitHub)
112     * That project also serves as storage for fonts that were made with Glamer, and appropriately-licensed fonts are
113     * added to the "premade" folder once they are converted.
114     * <br>
115     * If you don't know what font to pick, {@link DefaultResources#getCrispLeanFamily()} and
116     * {@link DefaultResources#getCrispSlabFamily()} have the same (very large) character coverage, and have bold and
117     * italic modes that can be accessed with {@link GDXMarkup} if you decide to use that later.
118     * @param textFactory the TextCellFactory to use for the font
119     * @param gridWidth the width of the display area in cells
120     * @param gridHeight the height of the display area in cells
121     * @param cellWidth the width of a single cell in pixels, before any stretching is applied
122     * @param cellHeight the height of a single cell in pixels, before any stretching is applied
123     */
124    public StarterKit(TextCellFactory textFactory, int gridWidth, int gridHeight, int cellWidth, int cellHeight) {
125        this(textFactory, gridWidth, gridHeight, cellWidth, cellHeight, 0, 0);
126    }
127    /**
128     * Constructs a StarterKit with the given width and height in cells (gridWidth and gridHeight) and the given width
129     * and height for each letter (cellWidth and cellHeight), using the given TextCellFactory for the font; this
130     * overload also allows specifying additional space in pixels to be added to the right or bottom sides of the area
131     * with the grid of chars. You can use any of the pre-constructed TextCellFactory objects in
132     * {@link DefaultResources}, such as {@link DefaultResources#getCrispLeanFamily()}
133     * {@link DefaultResources#getCrispDejaVuFont()}, {@link DefaultResources#getCrispSlabFamily()}, or
134     * {@link DefaultResources#getStretchableTypewriterFont()}, as long as you have the right assets available (their
135     * documentation says the exact files you need). While you can construct your own TextCellFactory given a
136     * BitmapFont, that won't work well as a distance field font unless you used some very unusual configuration making
137     * the font, so the font would only look good at one size or possibly a multiple of that size. The defaults are
138     * recommended for now; a separate project is used to make the distance field monospace fonts
139     * (<a href="https://github.com/tommyettinger/Glamer">tommyettinger/Glamer</a> on GitHub). That project also serves
140     * as storage for fonts that were made with Glamer, and appropriately-licensed fonts are added to the "premade"
141     * folder once they are converted.
142     * <br>
143     * If you don't know what font to pick, {@link DefaultResources#getCrispLeanFamily()} and
144     * {@link DefaultResources#getCrispSlabFamily()} have the same (very large) character coverage, and have bold and
145     * italic modes that can be accessed with {@link GDXMarkup} if you decide to use that later.
146     * @param textFactory the TextCellFactory to use for the font
147     * @param gridWidth the width of the display area in cells
148     * @param gridHeight the height of the display area in cells
149     * @param cellWidth the width of a single cell in pixels, before any stretching is applied
150     * @param cellHeight the height of a single cell in pixels, before any stretching is applied
151     * @param additionalWidth the width in pixels to add to the stretched area, before any stretching is applied
152     * @param additionalHeight the height in pixels to add to the stretched area, before any stretching is applied
153     */
154    public StarterKit(TextCellFactory textFactory, int gridWidth, int gridHeight, int cellWidth, int cellHeight,
155                      int additionalWidth, int additionalHeight) {
156        this.cellWidth = cellWidth;
157        this.cellHeight = cellHeight;
158        this.textFactory = textFactory == null ? DefaultResources.getCrispLeanFamily() : textFactory;
159        layers = new SparseLayers(gridWidth, gridHeight, this.cellWidth, this.cellHeight, this.textFactory);
160        this.gridWidth = gridWidth;
161        this.gridHeight = gridHeight;
162        batch = new FilterBatch();
163        viewport = new StretchViewport(this.cellWidth * gridWidth + additionalWidth, this.cellHeight * gridHeight + additionalHeight);
164        stage = new Stage(viewport, batch);
165        stage.addActor(layers);
166    }
167
168    /**
169     * Not a complete drawing solution; so much of the logic related to drawing is specific to each game, like
170     * FOV being used to make certain things not render if they are out of sight, that this doesn't even try to
171     * guess at what a particular game needs for its rendering code. Any {@link TextCellFactory.Glyph} objects in
172     * {@link #layers} will be rendered by that SparseLayers, but any that aren't stored in layers must be drawn
173     * separately (Glyph has a {@link TextCellFactory.Glyph#draw(Batch, float)} method that must be called between
174     * {@link Batch#begin()} and {@link Batch#end()}, typically with begin() called before all Glyphs are drawn in
175     * a loop and then with end() called after). 
176     * <br>
177     * Specifically, this applies the current viewport to the stage, draws the stage, and makes any actions or
178     * events related to the stage take effect. Should not be called inside a {@link FilterBatch#begin()} block,
179     * since this calls it itself by drawing the stage, and also calls {@link FilterBatch#end()} afterwards.
180     */
181    public void draw()
182    {
183        stage.getViewport().apply(true);
184        stage.act();
185        stage.draw();
186    }
187    
188    /**
189     * Not a complete resize method; this is meant to handle the resizing of this StarterKit only and should be
190     * called inside your main Game, ApplicationListener, etc. class' resize method.
191     * @param width the new width of the screen; should be a parameter from the other resize() method
192     * @param height the new height of the screen; should be a parameter from the other resize() method
193     */
194    public void resize(int width, int height) {
195        viewport.update(width, height, true);
196    }
197
198}