docs: Added some useless JavaDoc
This commit is contained in:
@ -14,6 +14,10 @@ import org.jline.terminal.TerminalBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The main class that starts the whole game. It initializes providers and sets
|
||||
* up a terminal and creates the main game loop.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
|
@ -25,6 +25,7 @@ import cz.jzitnik.game.ui.Inventory;
|
||||
import cz.jzitnik.game.handlers.rightclick.RightClickHandlerProvider;
|
||||
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import cz.jzitnik.game.stats.Stats;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@ -54,6 +55,7 @@ public class Game {
|
||||
private final Inventory inventory = new Inventory();
|
||||
private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
||||
private transient GameStates gameStates = new GameStates(this);
|
||||
private Stats stats = new Stats();
|
||||
|
||||
/** Current time of day in the game (0–600 range). */
|
||||
@Setter
|
||||
@ -114,6 +116,8 @@ public class Game {
|
||||
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
||||
screenRenderer.render(this);
|
||||
|
||||
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||
|
||||
entitySpawnProvider.update(this, terminal);
|
||||
|
||||
playMovePlayerSound(cords[0] + 1, cords[1]);
|
||||
@ -143,6 +147,8 @@ public class Game {
|
||||
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
||||
screenRenderer.render(this);
|
||||
|
||||
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||
|
||||
entitySpawnProvider.update(this, terminal);
|
||||
|
||||
playMovePlayerSound(cords[0] - 1, cords[1]);
|
||||
@ -170,6 +176,8 @@ public class Game {
|
||||
world[cords[1] - 2][cords[0]].add(player.getPlayerBlock1());
|
||||
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
||||
|
||||
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(400);
|
||||
@ -369,6 +377,8 @@ public class Game {
|
||||
}
|
||||
}
|
||||
|
||||
stats.setBlocksMined(stats.getBlocksMined() + 1);
|
||||
|
||||
screenRenderer.render(this);
|
||||
|
||||
update(screenRenderer);
|
||||
@ -460,6 +470,8 @@ public class Game {
|
||||
world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
|
||||
player.addFalling();
|
||||
|
||||
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||
|
||||
screenRenderer.render(this);
|
||||
} else {
|
||||
ArrayList<Block> combinedList = new ArrayList<>();
|
||||
@ -560,6 +572,7 @@ public class Game {
|
||||
}
|
||||
}
|
||||
gameStates.dependencies.eventHandlerProvider.handlePlace(screenRenderer, this, x, y);
|
||||
stats.setBlocksPlaced(stats.getBlocksPlaced() + 1);
|
||||
screenRenderer.render(this);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Handles saving and loading the game state using Kryo serialization.
|
||||
*/
|
||||
@Slf4j
|
||||
public class GameSaver {
|
||||
|
||||
@ -14,12 +17,20 @@ public class GameSaver {
|
||||
|
||||
private final Kryo kryo;
|
||||
|
||||
/**
|
||||
* Initializes the GameSaver with Kryo serialization setup.
|
||||
*/
|
||||
public GameSaver() {
|
||||
this.kryo = new Kryo();
|
||||
kryo.setRegistrationRequired(false);
|
||||
kryo.setReferences(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current game state to a file.
|
||||
*
|
||||
* @param game the game instance to be saved
|
||||
*/
|
||||
public void save(Game game) {
|
||||
try (Output output = new Output(new FileOutputStream(SAVE_FILE))) {
|
||||
kryo.writeClassAndObject(output, game);
|
||||
@ -28,6 +39,12 @@ public class GameSaver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the game state from a file. If the save file does not exist,
|
||||
* a new game instance is returned.
|
||||
*
|
||||
* @return the loaded game instance or a new game if no save is found
|
||||
*/
|
||||
public Game load() {
|
||||
log.info("Loading game");
|
||||
|
||||
|
@ -6,7 +6,6 @@ import cz.jzitnik.game.annotations.ItemRegistry;
|
||||
import cz.jzitnik.game.entities.items.Item;
|
||||
import cz.jzitnik.game.entities.items.ItemType;
|
||||
import cz.jzitnik.game.sprites.Dye;
|
||||
import cz.jzitnik.game.sprites.WoolItem;
|
||||
|
||||
@Fuel(0.5)
|
||||
@ItemRegistry("black_dye")
|
||||
|
11
src/main/java/cz/jzitnik/game/stats/Stats.java
Normal file
11
src/main/java/cz/jzitnik/game/stats/Stats.java
Normal file
@ -0,0 +1,11 @@
|
||||
package cz.jzitnik.game.stats;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Stats {
|
||||
private int secondsPlayed = 0;
|
||||
private int blocksPlaced = 0;
|
||||
private int blocksMined = 0;
|
||||
private int blocksTraveled = 0;
|
||||
}
|
21
src/main/java/cz/jzitnik/game/stats/StatsThread.java
Normal file
21
src/main/java/cz/jzitnik/game/stats/StatsThread.java
Normal file
@ -0,0 +1,21 @@
|
||||
package cz.jzitnik.game.stats;
|
||||
|
||||
import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||
|
||||
@ThreadRegistry
|
||||
public class StatsThread extends Thread {
|
||||
private Stats stats;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
stats.setSecondsPlayed(stats.getSecondsPlayed());
|
||||
|
||||
while (true) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,12 +7,16 @@ import java.util.Set;
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||
import cz.jzitnik.game.entities.Player;
|
||||
import cz.jzitnik.game.stats.Stats;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
/**
|
||||
* Scans and registers all threads annotated with {@link ThreadRegistry} and starts them when requested.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ThreadProvider {
|
||||
private final Game game;
|
||||
@ -21,6 +25,9 @@ public class ThreadProvider {
|
||||
private final boolean[] isRunning;
|
||||
private final List<Thread> list = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Starts all registered threads.
|
||||
*/
|
||||
public void start() {
|
||||
log.info("Loading all the threads");
|
||||
for (Thread thread : list) {
|
||||
@ -28,6 +35,14 @@ public class ThreadProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ThreadProvider and registers all eligible threads.
|
||||
*
|
||||
* @param game The game instance.
|
||||
* @param screenRenderer The screen renderer.
|
||||
* @param terminal The terminal.
|
||||
* @param isRunning A shared boolean array for controlling thread execution.
|
||||
*/
|
||||
public ThreadProvider(Game game, ScreenRenderer screenRenderer, Terminal terminal, boolean[] isRunning) {
|
||||
this.game = game;
|
||||
this.screenRenderer = screenRenderer;
|
||||
@ -36,6 +51,10 @@ public class ThreadProvider {
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the package for classes annotated with {@link ThreadRegistry} and instantiates valid threads.
|
||||
* Matches constructors using known parameter types (e.g., Game, Stats, Terminal, etc.).
|
||||
*/
|
||||
private void registerHandlers() {
|
||||
Reflections reflections = new Reflections("cz.jzitnik.game.threads");
|
||||
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(ThreadRegistry.class);
|
||||
@ -52,6 +71,8 @@ public class ThreadProvider {
|
||||
Class<?> type = paramTypes[i];
|
||||
if (type == Game.class)
|
||||
params[i] = game;
|
||||
if (type == Stats.class)
|
||||
params[i] = game.getStats();
|
||||
else if (type == ScreenRenderer.class)
|
||||
params[i] = screenRenderer;
|
||||
else if (type == Terminal.class)
|
||||
|
@ -4,11 +4,19 @@ import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||
import cz.jzitnik.game.entities.Player;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* A thread responsible for periodically regenerating the player's health.
|
||||
* Executes every 4 seconds and stops if the thread is interrupted.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@ThreadRegistry
|
||||
public class HealthRegenerationThread extends Thread {
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* Runs the health regeneration loop, healing the player every 4 seconds.
|
||||
* The loop continues until the thread is interrupted.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
|
@ -4,6 +4,10 @@ import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||
import cz.jzitnik.game.entities.Player;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* A thread that periodically reduces the player's hunger level.
|
||||
* Runs continuously, sleeping between hunger drain cycles.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@ThreadRegistry
|
||||
public class HungerDrainThread extends Thread {
|
||||
|
@ -13,6 +13,10 @@ import java.nio.BufferUnderflowException;
|
||||
import java.util.Optional;
|
||||
import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||
|
||||
/**
|
||||
* A thread that handles keyboard and mouse input from the terminal.
|
||||
* Responsible for routing input events to the appropriate handlers based on the current window.
|
||||
*/
|
||||
@ThreadRegistry
|
||||
public class InputHandlerThread extends Thread {
|
||||
private final Game game;
|
||||
@ -20,6 +24,13 @@ public class InputHandlerThread extends Thread {
|
||||
private final ScreenRenderer screenRenderer;
|
||||
private final MouseHandler mouseHandler;
|
||||
|
||||
/**
|
||||
* Constructs the input handler thread with required dependencies.
|
||||
*
|
||||
* @param game The game instance.
|
||||
* @param terminal The terminal to read input from.
|
||||
* @param screenRenderer Renderer to update the game screen.
|
||||
*/
|
||||
public InputHandlerThread(Game game, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
this.game = game;
|
||||
this.terminal = terminal;
|
||||
@ -27,6 +38,9 @@ public class InputHandlerThread extends Thread {
|
||||
this.mouseHandler = new MouseHandler(game, terminal, screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread run method that listens for keyboard and mouse events and dispatches them appropriately.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
@ -6,6 +6,10 @@ import cz.jzitnik.game.entities.Player;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* A thread that periodically checks the player's hunger level.
|
||||
* If hunger reaches 0, it applies damage to the player.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@ThreadRegistry
|
||||
public class NoHungerThread extends Thread {
|
||||
|
@ -4,12 +4,23 @@ import cz.jzitnik.game.Game;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Handles clean-up or exit operations when closing certain UI windows.
|
||||
*/
|
||||
public class CloseHandler {
|
||||
/**
|
||||
* A functional interface for handling window close operations on the game instance.
|
||||
*
|
||||
* @param <T> The type of the argument passed to the function.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Function<T> {
|
||||
void call(T t);
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of window types to their corresponding close handler functions.
|
||||
*/
|
||||
public static HashMap<Window, Function<Game>> handles = new HashMap<>();
|
||||
|
||||
static {
|
||||
@ -17,6 +28,12 @@ public class CloseHandler {
|
||||
handles.put(Window.INVENTORY, game -> game.getInventory().exit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the close handler function for the specified window, if one is registered.
|
||||
*
|
||||
* @param window The window being closed.
|
||||
* @param game The game instance.
|
||||
*/
|
||||
public static void handle(Window window, Game game) {
|
||||
if (handles.containsKey(window)) {
|
||||
var func = handles.get(window);
|
||||
|
@ -15,6 +15,10 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the crafting table UI where the player can combine items
|
||||
* according to recipes to create new ones.
|
||||
*/
|
||||
public class CraftingTable {
|
||||
private final Game game;
|
||||
private static final int ROW_AMOUNT = 3;
|
||||
@ -26,10 +30,20 @@ public class CraftingTable {
|
||||
private final InventoryItem[] items = new InventoryItem[ROW_AMOUNT * COLUMN_AMOUNT];
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Sets the current game window to the crafting table view.
|
||||
*
|
||||
* @param ignored Ignored parameter for compatibility.
|
||||
* @param ignored2 Ignored parameter for compatibility.
|
||||
*/
|
||||
public void render(int ignored, int ignored2) {
|
||||
game.setWindow(Window.CRAFTING_TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks up the result of a valid recipe if one exists in the crafting grid,
|
||||
* adds it to the inventory, and decreases the input items accordingly.
|
||||
*/
|
||||
public void pickup() {
|
||||
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
||||
.map(item -> item == null ? null : item.getItem().getFirst().getId()).toArray(String[]::new));
|
||||
@ -53,6 +67,13 @@ public class CraftingTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the crafting table UI and the inventory below it.
|
||||
*
|
||||
* @param buffer Buffer to append rendered content to.
|
||||
* @param terminal Terminal to determine screen dimensions.
|
||||
* @param spriteList Sprite list to fetch item visuals.
|
||||
*/
|
||||
public void render(StringBuilder buffer, Terminal terminal, SpriteList spriteList) {
|
||||
int widthPixels = COLUMN_AMOUNT * (CELL_WIDTH + BORDER_SIZE) + BORDER_SIZE;
|
||||
var inventory = game.getInventory();
|
||||
@ -117,6 +138,13 @@ public class CraftingTable {
|
||||
game.getInventory().renderFull(buffer, terminal, spriteList, false, Optional.of(size), game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse clicks inside the crafting grid or on the output slot.
|
||||
*
|
||||
* @param mouseEvent Mouse event with coordinates and type.
|
||||
* @param terminal Terminal for dimension reference.
|
||||
* @param screenRenderer Renderer to trigger screen updates.
|
||||
*/
|
||||
public void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
int x = mouseEvent.getX();
|
||||
int y = mouseEvent.getY();
|
||||
@ -154,6 +182,10 @@ public class CraftingTable {
|
||||
Optional.of(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers all remaining items in the crafting grid back to the inventory
|
||||
* and clears the crafting grid.
|
||||
*/
|
||||
public void exit() {
|
||||
// Put all items from crafting to inv
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
@ -167,6 +199,11 @@ public class CraftingTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for the crafting table.
|
||||
*
|
||||
* @param game Reference to the game instance.
|
||||
*/
|
||||
public CraftingTable(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ import cz.jzitnik.game.sprites.ui.Font.Size;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Displays the death screen UI with a "You Died" message and a respawn button.
|
||||
* Handles rendering and mouse interactions for the respawn action.
|
||||
*/
|
||||
@Slf4j
|
||||
public class DeathScreen {
|
||||
private int btnWidth;
|
||||
@ -21,6 +25,13 @@ public class DeathScreen {
|
||||
private int textButtonMargin;
|
||||
private boolean buttonHover;
|
||||
|
||||
/**
|
||||
* Renders the death screen with a heading and the respawn button.
|
||||
*
|
||||
* @param buffer The rendering buffer.
|
||||
* @param terminal The terminal to determine size and dimensions.
|
||||
* @param game The game instance for accessing dependencies.
|
||||
*/
|
||||
public void render(StringBuilder buffer, Terminal terminal, Game game) {
|
||||
log.debug("Rendering death screen");
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
@ -37,6 +48,16 @@ public class DeathScreen {
|
||||
renderButton(buffer, "Respawn", width, font, terminal, buttonHover);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a single button with hover state and styling.
|
||||
*
|
||||
* @param buffer The rendering buffer.
|
||||
* @param txt The text to display in the button.
|
||||
* @param width The terminal width for calculating centering.
|
||||
* @param font The font renderer.
|
||||
* @param terminal The terminal object.
|
||||
* @param selected Whether the button is currently hovered.
|
||||
*/
|
||||
public void renderButton(StringBuilder buffer, String txt, int width, Font font, Terminal terminal,
|
||||
boolean selected) {
|
||||
btnWidth = Math.min(350, (int) (width * (3.0 / 4)));
|
||||
@ -62,6 +83,15 @@ public class DeathScreen {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse interactions for the death screen, including hover and click
|
||||
* detection on the "Respawn" button.
|
||||
*
|
||||
* @param mouseEvent The mouse event that occurred.
|
||||
* @param terminal The terminal used for dimensions.
|
||||
* @param screenRenderer The screen renderer to trigger re-renders.
|
||||
* @param game The game instance to change state upon respawn.
|
||||
*/
|
||||
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game) {
|
||||
int x = mouseEvent.getX();
|
||||
int y = mouseEvent.getY();
|
||||
|
@ -7,28 +7,59 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import cz.jzitnik.tui.utils.Menu;
|
||||
|
||||
/**
|
||||
* Handles the in-game escape menu functionality, including rendering options,
|
||||
* handling mouse input, and managing save and exit logic.
|
||||
*/
|
||||
public class Escape {
|
||||
private Game game;
|
||||
private Menu menu;
|
||||
|
||||
/**
|
||||
* Constructs the Escape menu for the given game instance.
|
||||
*
|
||||
* @param game The current game instance.
|
||||
*/
|
||||
public Escape(Game game) {
|
||||
this.game = game;
|
||||
this.menu = new Menu(game, "2DCraft", new String[] { "Continue", "Options", "Save and exit" },
|
||||
this::onButtonClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the escape menu to its initial state.
|
||||
*/
|
||||
public void reset() {
|
||||
menu.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the escape menu onto the screen.
|
||||
*
|
||||
* @param buffer The buffer to which rendering output is appended.
|
||||
* @param terminal The terminal used to get display properties.
|
||||
*/
|
||||
public void render(StringBuilder buffer, Terminal terminal) {
|
||||
menu.render(buffer, terminal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse input events within the escape menu.
|
||||
*
|
||||
* @param mouseEvent The mouse event triggered by the user.
|
||||
* @param terminal The terminal used for input/output.
|
||||
* @param screenRenderer The screen renderer for drawing changes.
|
||||
*/
|
||||
public void mouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
menu.handleMouse(mouseEvent, terminal, screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a "Saving" screen with centered large-font text.
|
||||
*
|
||||
* @param buffer The buffer to which rendering output is appended.
|
||||
* @param terminal The terminal used to calculate screen size.
|
||||
*/
|
||||
private void onButtonClick(int index, ScreenRenderer screenRenderer) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
@ -49,6 +80,12 @@ public class Escape {
|
||||
screenRenderer.render(game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a "Saved" screen with centered large-font text.
|
||||
*
|
||||
* @param buffer The buffer to which rendering output is appended.
|
||||
* @param terminal The terminal used to calculate screen size.
|
||||
*/
|
||||
public void renderSave(StringBuilder buffer, Terminal terminal) {
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
var savingText = font.line(terminal, "Saving", Size.LARGE, Align.CENTER);
|
||||
@ -59,6 +96,12 @@ public class Escape {
|
||||
buffer.append(savingText.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a button in the escape menu is clicked.
|
||||
*
|
||||
* @param index The index of the clicked button (0 = Continue, 1 = Options, 2 = Save and exit).
|
||||
* @param screenRenderer The screen renderer used to refresh the display.
|
||||
*/
|
||||
public void renderSaved(StringBuilder buffer, Terminal terminal) {
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
var savingText = font.line(terminal, "Saved", Size.LARGE, Align.CENTER);
|
||||
|
@ -9,7 +9,24 @@ import org.jline.terminal.Terminal;
|
||||
|
||||
import static cz.jzitnik.game.ui.Inventory.INVENTORY_SIZE_PX;
|
||||
|
||||
/**
|
||||
* A utility class for rendering the player's health and hunger bars.
|
||||
* The bars are displayed in a graphical form using text sprites.
|
||||
*/
|
||||
public class Healthbar {
|
||||
|
||||
/**
|
||||
* Renders the health and hunger bar of the player using sprite graphics.
|
||||
*
|
||||
* <p>This method uses the terminal width to position the bars, and pulls sprite data from the
|
||||
* provided {@link SpriteList} to render the correct number of hearts and hunger icons
|
||||
* based on the player's current health and hunger levels.</p>
|
||||
*
|
||||
* @param buffer The StringBuilder to which the rendering output is appended.
|
||||
* @param spriteList The list of sprites used for rendering the hearts and hunger icons.
|
||||
* @param terminal The terminal from which screen width is obtained.
|
||||
* @param game The current game instance, from which player state is read.
|
||||
*/
|
||||
public static void render(StringBuilder buffer, SpriteList spriteList, Terminal terminal, Game game) {
|
||||
int termWidth = terminal.getWidth();
|
||||
int startLeft = Math.max(0, (termWidth / 2) - (INVENTORY_SIZE_PX / 2));
|
||||
|
@ -1,5 +0,0 @@
|
||||
package cz.jzitnik.game.ui;
|
||||
|
||||
public class HomeScreen {
|
||||
|
||||
}
|
@ -14,6 +14,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a player's inventory including hotbar, crafting table, and item management.
|
||||
*/
|
||||
@Getter
|
||||
public class Inventory {
|
||||
public static final int INVENTORY_SIZE_PX = 470;
|
||||
@ -32,6 +35,11 @@ public class Inventory {
|
||||
@Setter
|
||||
private boolean rightClick = false;
|
||||
|
||||
/**
|
||||
* Returns the currently held item in the hotbar's selected hand index.
|
||||
*
|
||||
* @return Optional containing the held Item if present, otherwise empty.
|
||||
*/
|
||||
public Optional<Item> getItemInHand() {
|
||||
if (hotbar[itemInhHandIndex] == null) {
|
||||
return Optional.empty();
|
||||
@ -39,6 +47,9 @@ public class Inventory {
|
||||
return Optional.of(hotbar[itemInhHandIndex].getItem().getLast());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the amount of the item in hand by one, removing it if the amount reaches zero.
|
||||
*/
|
||||
public void decreaseItemInHand() {
|
||||
if (hotbar[itemInhHandIndex] == null) {
|
||||
return;
|
||||
@ -52,6 +63,13 @@ public class Inventory {
|
||||
hotbar[itemInhHandIndex].decrease();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to place a new item into the inventory or hotbar.
|
||||
* If inventory and hotbar are full, the item is lost.
|
||||
*
|
||||
* @param item The item to place.
|
||||
*/
|
||||
private void placeItem(Item item) {
|
||||
var inventoryItem = new InventoryItem(item);
|
||||
|
||||
@ -74,18 +92,33 @@ public class Inventory {
|
||||
// If inventory is full the item is lost
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an InventoryItem's amount of items into the inventory.
|
||||
*
|
||||
* @param item InventoryItem to add.
|
||||
*/
|
||||
public void addItem(InventoryItem item) {
|
||||
for (int i = 0; i < item.getAmount(); i++) {
|
||||
addItem(item.getItem().get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of Items to the inventory.
|
||||
*
|
||||
* @param item List of Items to add.
|
||||
*/
|
||||
public void addItem(List<Item> item) {
|
||||
for (Item i : item) {
|
||||
addItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single Item to the inventory, stacking if possible.
|
||||
*
|
||||
* @param item Item to add.
|
||||
*/
|
||||
public void addItem(Item item) {
|
||||
if (!item.isStackable()) {
|
||||
placeItem(item);
|
||||
@ -113,6 +146,11 @@ public class Inventory {
|
||||
placeItem(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the background for the hotbar slots.
|
||||
*
|
||||
* @return Background sprite string.
|
||||
*/
|
||||
private String getHotbarBackground() {
|
||||
StringBuilder sprite = new StringBuilder();
|
||||
for (int i = 0; i < 25; i++) {
|
||||
@ -122,6 +160,15 @@ public class Inventory {
|
||||
return sprite.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the hotbar UI onto the given buffer.
|
||||
*
|
||||
* @param buffer StringBuilder buffer to append rendering output.
|
||||
* @param spriteList SpriteList used for rendering item sprites.
|
||||
* @param terminal Terminal object for screen dimensions.
|
||||
* @param isFull Whether full inventory mode is active.
|
||||
* @param game Current game instance.
|
||||
*/
|
||||
public void renderHotbar(StringBuilder buffer, SpriteList spriteList, Terminal terminal, boolean isFull, Game game) {
|
||||
int termWidth = terminal.getWidth();
|
||||
int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2);
|
||||
@ -151,8 +198,18 @@ public class Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the full inventory UI including items and crafting table.
|
||||
*
|
||||
* @param buffer StringBuilder buffer to append rendering output.
|
||||
* @param terminal Terminal object for screen dimensions.
|
||||
* @param spriteList SpriteList used for rendering item sprites.
|
||||
* @param includeCrafting Whether to include the crafting UI.
|
||||
* @param moveTopCustom Optional vertical offset.
|
||||
* @param game Current game instance.
|
||||
*/
|
||||
public void renderFull(StringBuilder buffer, Terminal terminal, SpriteList spriteList, boolean includeCrafting,
|
||||
Optional<Integer> moveTopCustom, Game game) {
|
||||
Optional<Integer> moveTopCustom, Game game) {
|
||||
int widthPixels = COLUMN_AMOUNT * (50 + 4) + 2;
|
||||
int heightPixels = ROW_AMOUNT * (25 + 1);
|
||||
|
||||
@ -214,6 +271,14 @@ public class Inventory {
|
||||
renderHotbar(buffer, spriteList, terminal, true, game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of sprite strings for the given inventory items, highlighting selected.
|
||||
* @param items Array of InventoryItem objects.
|
||||
* @param spriteList SpriteList used to retrieve sprites.
|
||||
* @param selectedItem Index of the selected item.
|
||||
* @param game Current game instance.
|
||||
* @return List of sprite strings representing each inventory slot.
|
||||
*/
|
||||
public List<String> getSprites(InventoryItem[] items, SpriteList spriteList, int selectedItem, Game game) {
|
||||
List<String> sprites = new ArrayList<>();
|
||||
|
||||
@ -255,16 +320,16 @@ public class Inventory {
|
||||
SpriteCombiner.combineTwoSprites(
|
||||
item.getItem().getFirst().getSpriteState().isPresent()
|
||||
? spriteList.getSprite(item.getItem().getFirst().getSprite())
|
||||
.getSprite(item.getItem().getFirst().getSpriteState()
|
||||
.get())
|
||||
.getSprite(item.getItem().getFirst().getSpriteState()
|
||||
.get())
|
||||
: spriteList.getSprite(item.getItem().getFirst().getSprite())
|
||||
.getSprite(),
|
||||
.getSprite(),
|
||||
Numbers.getNumberSprite(item.getAmount(), game)));
|
||||
} else {
|
||||
sprite = SpriteCombiner.combineTwoSprites(
|
||||
item.getItem().getFirst().getSpriteState().isPresent()
|
||||
? spriteList.getSprite(item.getItem().getFirst().getSprite())
|
||||
.getSprite(item.getItem().getFirst().getSpriteState().get())
|
||||
.getSprite(item.getItem().getFirst().getSpriteState().get())
|
||||
: spriteList.getSprite(item.getItem().getFirst().getSprite()).getSprite(),
|
||||
Numbers.getNumberSprite(item.getAmount(), game));
|
||||
}
|
||||
@ -280,6 +345,12 @@ public class Inventory {
|
||||
return sprites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an InventoryDTO wrapping the item array and index for the given absolute index.
|
||||
* @param index Index of the item in inventory/hotbar/crafting table.
|
||||
* @param i Optional custom InventoryItem array for special inventories.
|
||||
* @return InventoryDTO representing the selected inventory slot.
|
||||
*/
|
||||
public InventoryDTO getItem(int index, Optional<InventoryItem[]> i) {
|
||||
if (index < 20) {
|
||||
// Normal inventory
|
||||
@ -295,6 +366,11 @@ public class Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the InventoryItem at the selected index without modifying inventory.
|
||||
* @param i Optional custom InventoryItem array.
|
||||
* @return InventoryItem at the selected index or null if none.
|
||||
*/
|
||||
public InventoryItem getSelectedItemNo(Optional<InventoryItem[]> i) {
|
||||
InventoryDTO data = getItem(selectedItemInv, i);
|
||||
if (selectedItemInv == -1) {
|
||||
|
@ -10,7 +10,21 @@ import java.util.Optional;
|
||||
|
||||
import static cz.jzitnik.game.ui.Inventory.*;
|
||||
|
||||
/**
|
||||
* Handles mouse click events on the inventory UI, dispatching to appropriate
|
||||
* handlers for crafting table, hotbar, and main inventory.
|
||||
*/
|
||||
public class InventoryClickHandler {
|
||||
/**
|
||||
* Processes a mouse click event within the inventory UI.
|
||||
*
|
||||
* @param mouseEvent The mouse event to handle.
|
||||
* @param terminal Terminal instance for dimension calculations.
|
||||
* @param screenRenderer Renderer to update the screen after changes.
|
||||
* @param game Current game instance.
|
||||
* @param moveTopCustom Optional custom top offset.
|
||||
* @param i Optional inventory items array for manipulation.
|
||||
*/
|
||||
public static void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game,
|
||||
Optional<Integer> moveTopCustom, Optional<InventoryItem[]> i) {
|
||||
if (mouseEvent.getType() != MouseEvent.Type.Pressed)
|
||||
@ -98,6 +112,17 @@ public class InventoryClickHandler {
|
||||
screenRenderer.render(game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item clicks with left and right mouse button logic for picking up,
|
||||
* placing, and merging items.
|
||||
*
|
||||
* @param mouseEvent The mouse event triggered by the user.
|
||||
* @param inventory The inventory instance to modify.
|
||||
* @param items The item array being manipulated.
|
||||
* @param index The index of the clicked item.
|
||||
* @param offset The offset to apply to the index.
|
||||
* @param i Optional inventory items array for manipulation.
|
||||
*/
|
||||
public static void handleItemClick(MouseEvent mouseEvent, Inventory inventory, Object[] items, int index,
|
||||
int offset, Optional<InventoryItem[]> i) {
|
||||
int actualIndex = index + offset;
|
||||
|
@ -4,6 +4,9 @@ import cz.jzitnik.game.entities.items.InventoryItem;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for Inventory containing items and a selected index.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class InventoryDTO {
|
||||
|
@ -7,17 +7,34 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Represents the options menu where the user can configure settings
|
||||
* such as sound volume using a slider UI.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Options {
|
||||
private Game game;
|
||||
private int top;
|
||||
private int sliderWidth;
|
||||
private int sliderLeftpad;
|
||||
private int sliderLeftpad;
|
||||
|
||||
/**
|
||||
* Creates an Options menu for the given game instance.
|
||||
*
|
||||
* @param game the game instance providing configuration and resources
|
||||
*/
|
||||
public Options(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders the options menu UI including the heading, labels,
|
||||
* and the volume slider.
|
||||
*
|
||||
* @param buffer the string builder to append the rendered UI
|
||||
* @param terminal the terminal context for size and rendering info
|
||||
*/
|
||||
public void render(StringBuilder buffer, Terminal terminal) {
|
||||
var buf = new StringBuilder();
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
@ -40,6 +57,13 @@ public class Options {
|
||||
buffer.append(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the slider bar indicating the current volume percentage.
|
||||
*
|
||||
* @param buffer the string builder to append the slider UI
|
||||
* @param terminal the terminal context for width and rendering
|
||||
* @param percentage the current volume percentage to display
|
||||
*/
|
||||
private void renderSlider(StringBuilder buffer, Terminal terminal, int percentage) {
|
||||
var defaultColor = "\033[48;2;116;115;113m";
|
||||
var fullColor = "\033[48;2;70;70;70m";
|
||||
@ -63,6 +87,14 @@ public class Options {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse input events to update the sound volume based on
|
||||
* slider interaction.
|
||||
*
|
||||
* @param mouseEvent the mouse event containing position and type
|
||||
* @param terminal the terminal context for coordinate mapping
|
||||
* @param screenRenderer the renderer to update the screen after changes
|
||||
*/
|
||||
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
int x = mouseEvent.getX();
|
||||
int y = mouseEvent.getY() - top;
|
||||
|
@ -13,14 +13,30 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a small 2x2 crafting table that allows crafting of items
|
||||
* based on defined recipes.
|
||||
*/
|
||||
public class SmallCraftingTable {
|
||||
|
||||
/** Number of rows in the crafting grid. */
|
||||
public static final int ROW_AMOUNT = 2;
|
||||
|
||||
/** Number of columns in the crafting grid. */
|
||||
public static final int COLUMN_AMOUNT = 2;
|
||||
|
||||
/** The inventory associated with the crafting table. */
|
||||
private Inventory inventory;
|
||||
|
||||
/** The items currently placed in the crafting grid. */
|
||||
@Getter
|
||||
private InventoryItem[] items = new InventoryItem[4];
|
||||
|
||||
/**
|
||||
* Attempts to craft an item based on the items in the crafting grid.
|
||||
* If a matching recipe is found, the crafted item is added to the inventory,
|
||||
* and input items are consumed.
|
||||
*/
|
||||
public void pickup() {
|
||||
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
||||
.map(item -> item == null ? null : item.getItem().getFirst().getId()).toArray(String[]::new));
|
||||
@ -44,6 +60,13 @@ public class SmallCraftingTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the 2x2 crafting grid along with the result preview sprite.
|
||||
*
|
||||
* @param spriteList the list of available sprites
|
||||
* @param game the current game context
|
||||
* @return a string builder containing the ANSI-rendered crafting table
|
||||
*/
|
||||
public StringBuilder render(SpriteList spriteList, Game game) {
|
||||
var buf = new StringBuilder();
|
||||
|
||||
@ -105,6 +128,11 @@ public class SmallCraftingTable {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a small crafting table associated with the given inventory.
|
||||
*
|
||||
* @param inventory the inventory to use for storing crafted items
|
||||
*/
|
||||
public SmallCraftingTable(Inventory inventory) {
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
@ -1,5 +1,56 @@
|
||||
package cz.jzitnik.game.ui;
|
||||
|
||||
/**
|
||||
* Represents the different UI windows/screens available in the game.
|
||||
*/
|
||||
public enum Window {
|
||||
WORLD, INVENTORY, CRAFTING_TABLE, CHEST, FURNACE, ESC, SAVE_EXIT, SAVED, OPTIONS, DEATH_SCREEN,
|
||||
/**
|
||||
* Main game world view where the player moves and interacts.
|
||||
*/
|
||||
WORLD,
|
||||
|
||||
/**
|
||||
* Player's inventory screen.
|
||||
*/
|
||||
INVENTORY,
|
||||
|
||||
/**
|
||||
* Crafting table interface for combining items.
|
||||
*/
|
||||
CRAFTING_TABLE,
|
||||
|
||||
/**
|
||||
* Chest interface for item storage.
|
||||
*/
|
||||
CHEST,
|
||||
|
||||
/**
|
||||
* Furnace interface for smelting items.
|
||||
*/
|
||||
FURNACE,
|
||||
|
||||
/**
|
||||
* Escape/pause menu.
|
||||
*/
|
||||
ESC,
|
||||
|
||||
/**
|
||||
* Save and exit confirmation screen.
|
||||
*/
|
||||
SAVE_EXIT,
|
||||
|
||||
/**
|
||||
* Screen indicating the game has been successfully saved.
|
||||
*/
|
||||
SAVED,
|
||||
|
||||
/**
|
||||
* Options or settings menu.
|
||||
*/
|
||||
OPTIONS,
|
||||
|
||||
/**
|
||||
* Screen displayed when the player dies.
|
||||
*/
|
||||
DEATH_SCREEN,
|
||||
}
|
||||
|
@ -10,12 +10,21 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Handles mouse interactions in the terminal-based UI.
|
||||
* Supports clicking, scrolling, and moving to interact with blocks, inventory, and UI components.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MouseHandler {
|
||||
private Game game;
|
||||
private Terminal terminal;
|
||||
private ScreenRenderer screenRenderer;
|
||||
|
||||
/**
|
||||
* Handles a generic mouse event by dispatching to the correct handler based on the event type.
|
||||
*
|
||||
* @param mouseEvent The mouse event received from the terminal.
|
||||
*/
|
||||
public void handle(MouseEvent mouseEvent) {
|
||||
if (mouseEvent.getButton() == MouseEvent.Button.Button1 && mouseEvent.getType() == MouseEvent.Type.Pressed) {
|
||||
leftClick(mouseEvent);
|
||||
@ -37,6 +46,11 @@ public class MouseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse scroll events to change the selected hotbar slot when in the world view.
|
||||
*
|
||||
* @param mouseEvent The mouse scroll event.
|
||||
*/
|
||||
private void scroll(MouseEvent mouseEvent) {
|
||||
if (game.getWindow() == Window.WORLD) {
|
||||
if (mouseEvent.getButton() == MouseEvent.Button.WheelUp) {
|
||||
@ -57,6 +71,12 @@ public class MouseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles left-click actions on the screen.
|
||||
* Attempts to hit or mine the block the cursor is over, depending on the game state.
|
||||
*
|
||||
* @param mouseEvent The mouse click event (left button).
|
||||
*/
|
||||
private void leftClick(MouseEvent mouseEvent) {
|
||||
int mouseX = mouseEvent.getX();
|
||||
int mouseY = mouseEvent.getY();
|
||||
@ -87,6 +107,12 @@ public class MouseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles right-click actions on the screen.
|
||||
* Attempts to place a block at the cursor's current location.
|
||||
*
|
||||
* @param mouseEvent The mouse click event (right button).
|
||||
*/
|
||||
private void rightClick(MouseEvent mouseEvent) {
|
||||
int mouseX = mouseEvent.getX();
|
||||
int mouseY = mouseEvent.getY();
|
||||
@ -110,6 +136,12 @@ public class MouseHandler {
|
||||
game.build(blockX, blockY, screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse movement events.
|
||||
* Used for highlighting mineable blocks in the world view when hovering.
|
||||
*
|
||||
* @param mouseEvent The mouse move event.
|
||||
*/
|
||||
private void move(MouseEvent mouseEvent) {
|
||||
if (game.isMining() || game.getWindow() != Window.WORLD) {
|
||||
return;
|
||||
|
@ -6,8 +6,17 @@ import java.nio.charset.StandardCharsets;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Utility class responsible for loading text-based resources (e.g., ANSI art) from the classpath.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResourceLoader {
|
||||
/**
|
||||
* Loads a resource file as a UTF-8 encoded string from the <code>textures/</code> directory on the classpath.
|
||||
*
|
||||
* @param fileName Name of the file to load (relative to <code>textures/</code>).
|
||||
* @return Contents of the file as a string, or {@code null} if the file is not found or an I/O error occurs.
|
||||
*/
|
||||
public static String loadResource(String fileName) {
|
||||
log.info("Loading resource: {}", "textures/" + fileName);
|
||||
try (InputStream inputStream = ResourceLoader.class.getClassLoader()
|
||||
|
@ -1,6 +1,23 @@
|
||||
package cz.jzitnik.tui;
|
||||
|
||||
/**
|
||||
* Utility class that calculates the visible portion of the game world
|
||||
* based on player position and terminal dimensions.
|
||||
*/
|
||||
public class ScreenMovingCalculationProvider {
|
||||
/**
|
||||
* Calculates the boundaries of the visible world area (in block coordinates)
|
||||
* that should be rendered based on the terminal size and the player's position.
|
||||
*
|
||||
* @param x Player's X coordinate in the world.
|
||||
* @param y Player's Y coordinate in the world.
|
||||
* @param terminalHeight Height of the terminal in characters.
|
||||
* @param terminalWidth Width of the terminal in characters.
|
||||
* @param worldX Width of the game world in blocks.
|
||||
* @param worldY Height of the game world in blocks.
|
||||
* @return An array of four integers: [startX, endX, startY, endY],
|
||||
* representing the visible boundaries of the world.
|
||||
*/
|
||||
public static int[] calculate(int x, int y, int terminalHeight, int terminalWidth, int worldX, int worldY) {
|
||||
int spriteWidth = 50;
|
||||
int spriteHeight = 25;
|
||||
|
@ -19,20 +19,39 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Responsible for rendering the current game state to the terminal.
|
||||
* Handles multiple types of windows (e.g., world, inventory, furnace) and draws block sprites accordingly.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ScreenRenderer {
|
||||
private final SpriteList spriteList;
|
||||
private final Terminal terminal;
|
||||
|
||||
/**
|
||||
* Constructs a {@code ScreenRenderer} with the given sprite list and terminal.
|
||||
*
|
||||
* @param spriteList List of available sprites for rendering.
|
||||
* @param terminal Terminal instance used for output.
|
||||
*/
|
||||
public ScreenRenderer(SpriteList spriteList, Terminal terminal) {
|
||||
this.spriteList = spriteList;
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional coordinates of the currently selected block. Used for visual highlighting.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private Optional<List<Integer>> selectedBlock = Optional.empty();
|
||||
|
||||
/**
|
||||
* Finds the coordinates of the player's lower or upper body in the world array.
|
||||
*
|
||||
* @param world The 2D world array of blocks.
|
||||
* @return Integer array of coordinates [x, y], or null if player not found.
|
||||
*/
|
||||
private int[] getPlayerCords(List<Block>[][] world) {
|
||||
for (int i = 0; i < world.length; i++) {
|
||||
for (int j = 0; j < world[i].length; j++) {
|
||||
@ -51,6 +70,14 @@ public class ScreenRenderer {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the visual texture for a given block based on its type and state.
|
||||
*
|
||||
* @param block The block to render.
|
||||
* @param spriteList List of sprites.
|
||||
* @param game Game instance (needed for state checks).
|
||||
* @return String representing the block's rendered sprite.
|
||||
*/
|
||||
public String getTexture(Block block, SpriteList spriteList, Game game) {
|
||||
if (Air.class.isAssignableFrom(spriteList.getSprite(block.getSprite()).getClass())) {
|
||||
var air = (Air) spriteList.getSprite(block.getSprite());
|
||||
@ -64,6 +91,12 @@ public class ScreenRenderer {
|
||||
return spriteList.getSprite(block.getSprite()).getSprite(block.getSpriteState().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the entire screen based on the current game window.
|
||||
* Uses ANSI escape codes to clear and redraw the terminal content.
|
||||
*
|
||||
* @param game Current game state to render.
|
||||
*/
|
||||
public synchronized void render(Game game) {
|
||||
log.debug("Rendering frame");
|
||||
var world = game.getWorld();
|
||||
@ -185,6 +218,11 @@ public class ScreenRenderer {
|
||||
System.out.println(main);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a bordered visual block (used for highlighting selected blocks).
|
||||
*
|
||||
* @return A StringBuilder containing a sprite-like bordered area.
|
||||
*/
|
||||
private static StringBuilder getStringBuilder() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
|
@ -5,8 +5,35 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The main sound provider for the game.
|
||||
* <p>
|
||||
* This class is responsible for loading and playing all in-game sounds
|
||||
* from the {@code resources/sounds/} directory using the Java Sound API.
|
||||
* It allows different sound categories to have individual volume levels,
|
||||
* while also respecting a global master volume.
|
||||
* </p>
|
||||
*/
|
||||
@Slf4j
|
||||
public class SoundPlayer {
|
||||
/**
|
||||
* Plays a sound file from the resources directory.
|
||||
* <p>
|
||||
* The method supports 16-bit PCM signed audio. It converts the input audio
|
||||
* format to a standard PCM format, applies volume adjustment based on both
|
||||
* the backend category volume and the user-defined master volume, and plays
|
||||
* the result through the system's audio output.
|
||||
* </p>
|
||||
*
|
||||
* @param filePath the relative file path of the sound (within the {@code sounds/} folder),
|
||||
* e.g. {@code "step.ogg"}
|
||||
* @param backendVolume the volume specific to the sound category (e.g. footsteps vs. mining),
|
||||
* ranging from 0 (mute) to 100 (full volume)
|
||||
* @param masterVolume the global volume defined in the game options, ranging from 0 to 100
|
||||
* @throws LineUnavailableException if a suitable audio line cannot be opened
|
||||
* @throws IOException if an I/O error occurs during reading
|
||||
* @throws UnsupportedAudioFileException if the audio format is not supported
|
||||
*/
|
||||
public static void playSound(String filePath, int backendVolume, int masterVolume)
|
||||
throws LineUnavailableException, IOException, UnsupportedAudioFileException {
|
||||
if (!filePath.endsWith(".ogg") || masterVolume == 0) {
|
||||
@ -54,7 +81,19 @@ public class SoundPlayer {
|
||||
dataIn.close();
|
||||
audioStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Applies volume adjustment to a buffer of 16-bit PCM audio samples.
|
||||
* <p>
|
||||
* This method modifies the input byte buffer in place by scaling each 16-bit
|
||||
* signed sample by the given volume factor. Each sample is assumed to be in
|
||||
* little-endian byte order (least significant byte first).
|
||||
* </p>
|
||||
*
|
||||
* @param buffer the byte array containing 16-bit PCM audio samples
|
||||
* @param bytesRead the number of bytes read into the buffer; should be a multiple of 2
|
||||
* @param volume the volume multiplier (e.g., 0.5 for half volume, 2.0 for double volume)
|
||||
*/
|
||||
private static void applyVolume(byte[] buffer, int bytesRead, float volume) {
|
||||
for (int i = 0; i < bytesRead; i += 2) { // 16-bit PCM samples are 2 bytes each
|
||||
int sample = (buffer[i] & 0xFF) | (buffer[i + 1] << 8);
|
||||
|
@ -3,6 +3,12 @@ package cz.jzitnik.tui;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Abstract class representing a sprite that can have multiple visual states.
|
||||
* Each state is represented by a resource loaded from a specified location.
|
||||
*
|
||||
* @param <E> Enum type representing different states of the sprite.
|
||||
*/
|
||||
public abstract class Sprite<E extends Enum<E>> {
|
||||
private HashMap<E, String> resourcesLocation;
|
||||
private HashMap<E, Optional<String>> resources;
|
||||
@ -10,6 +16,12 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
private String defaultResourceLocation;
|
||||
private Class<E> enumClass;
|
||||
|
||||
/**
|
||||
* Loads the resource locations for each sprite state.
|
||||
*
|
||||
* @param values A map from enum keys to resource locations.
|
||||
* @param enumClass Class object of the enum used for states.
|
||||
*/
|
||||
protected final void loadResources(HashMap<E, String> values, Class<E> enumClass) {
|
||||
resources = new HashMap<>();
|
||||
resourcesLocation = new HashMap<>();
|
||||
@ -22,6 +34,12 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
this.enumClass = enumClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the resource for a specific sprite state from its location.
|
||||
*
|
||||
* @param key Enum key representing the sprite state.
|
||||
* @return Loaded resource as a String.
|
||||
*/
|
||||
private String loadResource(E key) {
|
||||
var location = resourcesLocation.get(key);
|
||||
var resource = ResourceLoader.loadResource(location);
|
||||
@ -31,12 +49,21 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the default sprite resource.
|
||||
*
|
||||
* @param resource Path to the default resource.
|
||||
*/
|
||||
protected final void loadResource(String resource) {
|
||||
defaultResourceLocation = resource;
|
||||
}
|
||||
|
||||
private final String loadResource() {
|
||||
/**
|
||||
* Loads the default sprite resource from its location.
|
||||
*
|
||||
* @return Loaded default resource as a String.
|
||||
*/
|
||||
private String loadResource() {
|
||||
var resource = ResourceLoader.loadResource(defaultResourceLocation);
|
||||
|
||||
defaultResource = resource;
|
||||
@ -44,6 +71,13 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loaded resource for the given sprite state.
|
||||
* If not loaded yet, it is loaded and cached.
|
||||
*
|
||||
* @param key Enum key representing the sprite state.
|
||||
* @return Resource as a String.
|
||||
*/
|
||||
protected final String getResource(E key) {
|
||||
var resource = resources.get(key);
|
||||
|
||||
@ -54,6 +88,12 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loaded default resource.
|
||||
* If not loaded yet, it is loaded and cached.
|
||||
*
|
||||
* @return Default resource as a String.
|
||||
*/
|
||||
protected final String getResource() {
|
||||
if (defaultResource != null) {
|
||||
return defaultResource;
|
||||
@ -61,11 +101,27 @@ public abstract class Sprite<E extends Enum<E>> {
|
||||
return loadResource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sprite for the default state.
|
||||
*
|
||||
* @return String representing the sprite.
|
||||
*/
|
||||
public abstract String getSprite();
|
||||
|
||||
/**
|
||||
* Returns the sprite for a given state.
|
||||
*
|
||||
* @param key Enum key representing the sprite state.
|
||||
* @return String representing the sprite.
|
||||
*/
|
||||
public abstract String getSprite(E key);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the enum class used to represent sprite states, if available. This function is used for automated tests.
|
||||
*
|
||||
* @return Optional containing the enum class, or empty if not set.
|
||||
*/
|
||||
public Optional<Class<E>> getStates() {
|
||||
return Optional.ofNullable(enumClass);
|
||||
}
|
||||
|
@ -3,9 +3,22 @@ package cz.jzitnik.tui;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A container class that holds a mapping from enum keys to their corresponding {@link Sprite} instances.
|
||||
* Ensures that a sprite is defined for every enum constant.
|
||||
*
|
||||
* @param <E> Enum type representing different sprite categories or states.
|
||||
*/
|
||||
public class SpriteList<E extends Enum<E>> {
|
||||
private final EnumMap<E, Sprite> sprites;
|
||||
|
||||
/**
|
||||
* Constructs a {@code SpriteList} and initializes it with the provided sprite mapping.
|
||||
* Throws a {@link RuntimeException} if any enum constant is missing in the initial map.
|
||||
*
|
||||
* @param enumClass The enum class used for sprite keys.
|
||||
* @param initialMap A map containing sprite instances for each enum constant.
|
||||
*/
|
||||
public SpriteList(Class<E> enumClass, HashMap<E, Sprite> initialMap) {
|
||||
sprites = new EnumMap<>(enumClass);
|
||||
|
||||
@ -18,6 +31,12 @@ public class SpriteList<E extends Enum<E>> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sprite associated with the given enum key.
|
||||
*
|
||||
* @param key Enum key representing the sprite category.
|
||||
* @return The corresponding {@link Sprite} instance.
|
||||
*/
|
||||
public Sprite getSprite(E key) {
|
||||
return sprites.get(key);
|
||||
}
|
||||
|
@ -10,6 +10,12 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Represents a simple interactive menu rendered in a terminal UI.
|
||||
* Displays a heading and a list of buttons, handles rendering and mouse interactions.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Menu {
|
||||
private Game game;
|
||||
@ -23,6 +29,14 @@ public class Menu {
|
||||
private int textButtonMargin;
|
||||
private ButtonClickHandler buttonClickHandler;
|
||||
|
||||
/**
|
||||
* Constructs a new Menu.
|
||||
*
|
||||
* @param game The game instance for accessing shared resources.
|
||||
* @param headingText The main heading to display at the top of the menu.
|
||||
* @param buttonLabels Array of button labels.
|
||||
* @param buttonClickHandler Handler for button click actions.
|
||||
*/
|
||||
public Menu(Game game, String headingText, String[] buttonLabels, ButtonClickHandler buttonClickHandler) {
|
||||
this.game = game;
|
||||
this.headingText = headingText;
|
||||
@ -31,24 +45,38 @@ public class Menu {
|
||||
this.buttonClickHandler = buttonClickHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the full menu including heading and buttons into the provided buffer.
|
||||
*
|
||||
* @param buffer The StringBuilder buffer to append rendered output to.
|
||||
* @param terminal The terminal instance, used to get terminal size.
|
||||
*/
|
||||
public void render(StringBuilder buffer, Terminal terminal) {
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
var width = terminal.getWidth();
|
||||
var height = terminal.getHeight();
|
||||
|
||||
|
||||
var heading = font.line(terminal, headingText, Size.LARGE, Align.CENTER);
|
||||
buffer.append(heading.getData());
|
||||
mainTextHeight = heading.getHeight();
|
||||
|
||||
textButtonMargin = (height < 600) ? 15 : (height < 800) ? 30 : 50;
|
||||
buffer.append("\n".repeat(textButtonMargin));
|
||||
|
||||
|
||||
for (int i = 0; i < buttonLabels.length; i++) {
|
||||
renderButton(buffer, buttonLabels[i], width, font, terminal, buttonsHover[i]);
|
||||
buffer.append("\n".repeat(textButtonMargin / 2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse events related to the menu.
|
||||
* Supports hover and click detection on buttons.
|
||||
*
|
||||
* @param mouseEvent The mouse event to handle.
|
||||
* @param terminal The terminal instance.
|
||||
* @param screenRenderer The renderer used to re-render the screen on hover change or click.
|
||||
*/
|
||||
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
int x = mouseEvent.getX();
|
||||
int y = mouseEvent.getY();
|
||||
@ -86,6 +114,16 @@ public class Menu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a single button to the buffer using the custom font renderer and styles.
|
||||
*
|
||||
* @param buffer The buffer to append rendered content to.
|
||||
* @param txt The text label of the button.
|
||||
* @param width The terminal width, used for calculating centering.
|
||||
* @param font The font rendering utility.
|
||||
* @param terminal The terminal instance.
|
||||
* @param selected Whether the button is currently hovered (for styling).
|
||||
*/
|
||||
public void renderButton(StringBuilder buffer, String txt, int width, Font font, Terminal terminal, boolean selected) {
|
||||
btnWidth = Math.min(350, (int) (width * (3.0 / 4)));
|
||||
leftPad = (width - btnWidth) / 2;
|
||||
@ -109,13 +147,23 @@ public class Menu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for handling button click actions.
|
||||
*/
|
||||
public interface ButtonClickHandler {
|
||||
/**
|
||||
* Called when a button is clicked.
|
||||
*
|
||||
* @param index The index of the clicked button.
|
||||
* @param screenRenderer The renderer used to update the screen.
|
||||
*/
|
||||
void onClick(int index, ScreenRenderer screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all hover states on the buttons (useful when switching views).
|
||||
*/
|
||||
public void reset() {
|
||||
for (int i = 0; i < buttonsHover.length; i++) {
|
||||
buttonsHover[i] = false;
|
||||
}
|
||||
Arrays.fill(buttonsHover, false);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,20 @@ package cz.jzitnik.tui.utils;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
|
||||
/**
|
||||
* Utility class for rendering numeric sprites (e.g. hotbar numbers) in the terminal UI.
|
||||
* Ensures fixed-size output suitable for consistent rendering in UI components like hotbars.
|
||||
*/
|
||||
public class Numbers {
|
||||
/**
|
||||
* Pads and formats a rendered number string to fit exactly into a 50x25 cell block,
|
||||
* typically used in the hotbar UI.
|
||||
*
|
||||
* @param str The raw rendered number string (multi-line).
|
||||
* @param height The actual height of the rendered string.
|
||||
* @param width The actual width of the rendered string.
|
||||
* @return A formatted and padded string of exactly 50x25 characters.
|
||||
*/
|
||||
public static String fixForHotbar(String str, int height, int width) {
|
||||
var parts = str.split("\n");
|
||||
// Make it 50x25
|
||||
@ -24,6 +37,14 @@ public class Numbers {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the number formatted as a 50x25 character sprite.
|
||||
* This is used to display hotbar slot numbers in a consistent visual size.
|
||||
*
|
||||
* @param number The number to render (1–9).
|
||||
* @param game The game context, used to retrieve font resources.
|
||||
* @return The formatted and fixed-size sprite string representing the number.
|
||||
*/
|
||||
public static String getNumberSprite(int number, Game game) {
|
||||
if (number == 1) {
|
||||
StringBuilder sprite = new StringBuilder();
|
||||
|
@ -1,6 +1,18 @@
|
||||
package cz.jzitnik.tui.utils;
|
||||
|
||||
/**
|
||||
* Utility class for combining ANSI-colored text sprites line-by-line,
|
||||
* preserving non-transparent pixels (those that are not "empty").
|
||||
* Useful for rendering layers (e.g. items over blocks) in a terminal UI.
|
||||
*/
|
||||
public class SpriteCombiner {
|
||||
/**
|
||||
* Combines multiple ANSI-colored text sprites into a single sprite.
|
||||
* Later sprites overlay earlier ones, preserving non-transparent characters.
|
||||
*
|
||||
* @param sprites An array of ANSI-formatted sprite strings.
|
||||
* @return A single string representing the combined sprite.
|
||||
*/
|
||||
public static String combineSprites(String[] sprites) {
|
||||
if (sprites == null || sprites.length == 0) {
|
||||
return "";
|
||||
@ -15,6 +27,14 @@ public class SpriteCombiner {
|
||||
return combinedSprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines two ANSI-formatted sprites into one, line by line.
|
||||
* Characters from {@code sprite2} are used unless they are transparent (spaces with \033[0m or \033[49m).
|
||||
*
|
||||
* @param sprite1 The base (background) sprite.
|
||||
* @param sprite2 The overlay (foreground) sprite.
|
||||
* @return A string representing the resulting combined sprite.
|
||||
*/
|
||||
public static String combineTwoSprites(String sprite1, String sprite2) {
|
||||
String[] rows1 = sprite1.split("\n");
|
||||
String[] rows2 = sprite2.split("\n");
|
||||
@ -69,6 +89,13 @@ public class SpriteCombiner {
|
||||
return combinedSprite.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively finds the last ANSI escape code preceding the given index.
|
||||
*
|
||||
* @param row The sprite row string.
|
||||
* @param index The index to search from.
|
||||
* @return The ANSI color code at or before the index.
|
||||
*/
|
||||
private static String getColorCode(String row, int index) {
|
||||
if (row.charAt(index) != '\033') {
|
||||
return getColorCode(row, index - 1);
|
||||
@ -77,6 +104,13 @@ public class SpriteCombiner {
|
||||
return extractColorCode(row, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an ANSI color escape code starting at the specified index in a row.
|
||||
*
|
||||
* @param row The sprite row string.
|
||||
* @param index The starting index (should be at '\033').
|
||||
* @return The full ANSI escape code (e.g., {@code "\033[48;2;255;255;255m"}).
|
||||
*/
|
||||
private static String extractColorCode(String row, int index) {
|
||||
StringBuilder colorCode = new StringBuilder();
|
||||
|
||||
|
Reference in New Issue
Block a user