docs: Added some useless JavaDoc
This commit is contained in:
@ -14,6 +14,10 @@ import org.jline.terminal.TerminalBuilder;
|
|||||||
|
|
||||||
import java.io.IOException;
|
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
|
@Slf4j
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
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.game.handlers.rightclick.RightClickHandlerProvider;
|
||||||
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
|
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
|
||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
|
import cz.jzitnik.game.stats.Stats;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ public class Game {
|
|||||||
private final Inventory inventory = new Inventory();
|
private final Inventory inventory = new Inventory();
|
||||||
private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
||||||
private transient GameStates gameStates = new GameStates(this);
|
private transient GameStates gameStates = new GameStates(this);
|
||||||
|
private Stats stats = new Stats();
|
||||||
|
|
||||||
/** Current time of day in the game (0–600 range). */
|
/** Current time of day in the game (0–600 range). */
|
||||||
@Setter
|
@Setter
|
||||||
@ -114,6 +116,8 @@ public class Game {
|
|||||||
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
||||||
screenRenderer.render(this);
|
screenRenderer.render(this);
|
||||||
|
|
||||||
|
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||||
|
|
||||||
entitySpawnProvider.update(this, terminal);
|
entitySpawnProvider.update(this, terminal);
|
||||||
|
|
||||||
playMovePlayerSound(cords[0] + 1, cords[1]);
|
playMovePlayerSound(cords[0] + 1, cords[1]);
|
||||||
@ -143,6 +147,8 @@ public class Game {
|
|||||||
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
||||||
screenRenderer.render(this);
|
screenRenderer.render(this);
|
||||||
|
|
||||||
|
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||||
|
|
||||||
entitySpawnProvider.update(this, terminal);
|
entitySpawnProvider.update(this, terminal);
|
||||||
|
|
||||||
playMovePlayerSound(cords[0] - 1, cords[1]);
|
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] - 2][cords[0]].add(player.getPlayerBlock1());
|
||||||
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
||||||
|
|
||||||
|
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(400);
|
Thread.sleep(400);
|
||||||
@ -369,6 +377,8 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stats.setBlocksMined(stats.getBlocksMined() + 1);
|
||||||
|
|
||||||
screenRenderer.render(this);
|
screenRenderer.render(this);
|
||||||
|
|
||||||
update(screenRenderer);
|
update(screenRenderer);
|
||||||
@ -460,6 +470,8 @@ public class Game {
|
|||||||
world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
|
world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
|
||||||
player.addFalling();
|
player.addFalling();
|
||||||
|
|
||||||
|
stats.setBlocksTraveled(stats.getBlocksTraveled() + 1);
|
||||||
|
|
||||||
screenRenderer.render(this);
|
screenRenderer.render(this);
|
||||||
} else {
|
} else {
|
||||||
ArrayList<Block> combinedList = new ArrayList<>();
|
ArrayList<Block> combinedList = new ArrayList<>();
|
||||||
@ -560,6 +572,7 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
gameStates.dependencies.eventHandlerProvider.handlePlace(screenRenderer, this, x, y);
|
gameStates.dependencies.eventHandlerProvider.handlePlace(screenRenderer, this, x, y);
|
||||||
|
stats.setBlocksPlaced(stats.getBlocksPlaced() + 1);
|
||||||
screenRenderer.render(this);
|
screenRenderer.render(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles saving and loading the game state using Kryo serialization.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GameSaver {
|
public class GameSaver {
|
||||||
|
|
||||||
@ -14,12 +17,20 @@ public class GameSaver {
|
|||||||
|
|
||||||
private final Kryo kryo;
|
private final Kryo kryo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the GameSaver with Kryo serialization setup.
|
||||||
|
*/
|
||||||
public GameSaver() {
|
public GameSaver() {
|
||||||
this.kryo = new Kryo();
|
this.kryo = new Kryo();
|
||||||
kryo.setRegistrationRequired(false);
|
kryo.setRegistrationRequired(false);
|
||||||
kryo.setReferences(true);
|
kryo.setReferences(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current game state to a file.
|
||||||
|
*
|
||||||
|
* @param game the game instance to be saved
|
||||||
|
*/
|
||||||
public void save(Game game) {
|
public void save(Game game) {
|
||||||
try (Output output = new Output(new FileOutputStream(SAVE_FILE))) {
|
try (Output output = new Output(new FileOutputStream(SAVE_FILE))) {
|
||||||
kryo.writeClassAndObject(output, game);
|
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() {
|
public Game load() {
|
||||||
log.info("Loading game");
|
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.Item;
|
||||||
import cz.jzitnik.game.entities.items.ItemType;
|
import cz.jzitnik.game.entities.items.ItemType;
|
||||||
import cz.jzitnik.game.sprites.Dye;
|
import cz.jzitnik.game.sprites.Dye;
|
||||||
import cz.jzitnik.game.sprites.WoolItem;
|
|
||||||
|
|
||||||
@Fuel(0.5)
|
@Fuel(0.5)
|
||||||
@ItemRegistry("black_dye")
|
@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.Game;
|
||||||
import cz.jzitnik.game.annotations.ThreadRegistry;
|
import cz.jzitnik.game.annotations.ThreadRegistry;
|
||||||
import cz.jzitnik.game.entities.Player;
|
import cz.jzitnik.game.entities.Player;
|
||||||
|
import cz.jzitnik.game.stats.Stats;
|
||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.jline.terminal.Terminal;
|
import org.jline.terminal.Terminal;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans and registers all threads annotated with {@link ThreadRegistry} and starts them when requested.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ThreadProvider {
|
public class ThreadProvider {
|
||||||
private final Game game;
|
private final Game game;
|
||||||
@ -21,6 +25,9 @@ public class ThreadProvider {
|
|||||||
private final boolean[] isRunning;
|
private final boolean[] isRunning;
|
||||||
private final List<Thread> list = new ArrayList<>();
|
private final List<Thread> list = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts all registered threads.
|
||||||
|
*/
|
||||||
public void start() {
|
public void start() {
|
||||||
log.info("Loading all the threads");
|
log.info("Loading all the threads");
|
||||||
for (Thread thread : list) {
|
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) {
|
public ThreadProvider(Game game, ScreenRenderer screenRenderer, Terminal terminal, boolean[] isRunning) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.screenRenderer = screenRenderer;
|
this.screenRenderer = screenRenderer;
|
||||||
@ -36,6 +51,10 @@ public class ThreadProvider {
|
|||||||
registerHandlers();
|
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() {
|
private void registerHandlers() {
|
||||||
Reflections reflections = new Reflections("cz.jzitnik.game.threads");
|
Reflections reflections = new Reflections("cz.jzitnik.game.threads");
|
||||||
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(ThreadRegistry.class);
|
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(ThreadRegistry.class);
|
||||||
@ -52,6 +71,8 @@ public class ThreadProvider {
|
|||||||
Class<?> type = paramTypes[i];
|
Class<?> type = paramTypes[i];
|
||||||
if (type == Game.class)
|
if (type == Game.class)
|
||||||
params[i] = game;
|
params[i] = game;
|
||||||
|
if (type == Stats.class)
|
||||||
|
params[i] = game.getStats();
|
||||||
else if (type == ScreenRenderer.class)
|
else if (type == ScreenRenderer.class)
|
||||||
params[i] = screenRenderer;
|
params[i] = screenRenderer;
|
||||||
else if (type == Terminal.class)
|
else if (type == Terminal.class)
|
||||||
|
@ -4,11 +4,19 @@ import cz.jzitnik.game.annotations.ThreadRegistry;
|
|||||||
import cz.jzitnik.game.entities.Player;
|
import cz.jzitnik.game.entities.Player;
|
||||||
import lombok.AllArgsConstructor;
|
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
|
@AllArgsConstructor
|
||||||
@ThreadRegistry
|
@ThreadRegistry
|
||||||
public class HealthRegenerationThread extends Thread {
|
public class HealthRegenerationThread extends Thread {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the health regeneration loop, healing the player every 4 seconds.
|
||||||
|
* The loop continues until the thread is interrupted.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -4,6 +4,10 @@ import cz.jzitnik.game.annotations.ThreadRegistry;
|
|||||||
import cz.jzitnik.game.entities.Player;
|
import cz.jzitnik.game.entities.Player;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread that periodically reduces the player's hunger level.
|
||||||
|
* Runs continuously, sleeping between hunger drain cycles.
|
||||||
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ThreadRegistry
|
@ThreadRegistry
|
||||||
public class HungerDrainThread extends Thread {
|
public class HungerDrainThread extends Thread {
|
||||||
|
@ -13,6 +13,10 @@ import java.nio.BufferUnderflowException;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import cz.jzitnik.game.annotations.ThreadRegistry;
|
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
|
@ThreadRegistry
|
||||||
public class InputHandlerThread extends Thread {
|
public class InputHandlerThread extends Thread {
|
||||||
private final Game game;
|
private final Game game;
|
||||||
@ -20,6 +24,13 @@ public class InputHandlerThread extends Thread {
|
|||||||
private final ScreenRenderer screenRenderer;
|
private final ScreenRenderer screenRenderer;
|
||||||
private final MouseHandler mouseHandler;
|
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) {
|
public InputHandlerThread(Game game, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.terminal = terminal;
|
this.terminal = terminal;
|
||||||
@ -27,6 +38,9 @@ public class InputHandlerThread extends Thread {
|
|||||||
this.mouseHandler = new MouseHandler(game, terminal, screenRenderer);
|
this.mouseHandler = new MouseHandler(game, terminal, screenRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread run method that listens for keyboard and mouse events and dispatches them appropriately.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -6,6 +6,10 @@ import cz.jzitnik.game.entities.Player;
|
|||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread that periodically checks the player's hunger level.
|
||||||
|
* If hunger reaches 0, it applies damage to the player.
|
||||||
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ThreadRegistry
|
@ThreadRegistry
|
||||||
public class NoHungerThread extends Thread {
|
public class NoHungerThread extends Thread {
|
||||||
|
@ -4,12 +4,23 @@ import cz.jzitnik.game.Game;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clean-up or exit operations when closing certain UI windows.
|
||||||
|
*/
|
||||||
public class CloseHandler {
|
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
|
@FunctionalInterface
|
||||||
public interface Function<T> {
|
public interface Function<T> {
|
||||||
void call(T 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<>();
|
public static HashMap<Window, Function<Game>> handles = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -17,6 +28,12 @@ public class CloseHandler {
|
|||||||
handles.put(Window.INVENTORY, game -> game.getInventory().exit());
|
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) {
|
public static void handle(Window window, Game game) {
|
||||||
if (handles.containsKey(window)) {
|
if (handles.containsKey(window)) {
|
||||||
var func = handles.get(window);
|
var func = handles.get(window);
|
||||||
|
@ -15,6 +15,10 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
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 {
|
public class CraftingTable {
|
||||||
private final Game game;
|
private final Game game;
|
||||||
private static final int ROW_AMOUNT = 3;
|
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 final InventoryItem[] items = new InventoryItem[ROW_AMOUNT * COLUMN_AMOUNT];
|
||||||
private int size;
|
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) {
|
public void render(int ignored, int ignored2) {
|
||||||
game.setWindow(Window.CRAFTING_TABLE);
|
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() {
|
public void pickup() {
|
||||||
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
||||||
.map(item -> item == null ? null : item.getItem().getFirst().getId()).toArray(String[]::new));
|
.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) {
|
public void render(StringBuilder buffer, Terminal terminal, SpriteList spriteList) {
|
||||||
int widthPixels = COLUMN_AMOUNT * (CELL_WIDTH + BORDER_SIZE) + BORDER_SIZE;
|
int widthPixels = COLUMN_AMOUNT * (CELL_WIDTH + BORDER_SIZE) + BORDER_SIZE;
|
||||||
var inventory = game.getInventory();
|
var inventory = game.getInventory();
|
||||||
@ -117,6 +138,13 @@ public class CraftingTable {
|
|||||||
game.getInventory().renderFull(buffer, terminal, spriteList, false, Optional.of(size), game);
|
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) {
|
public void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||||
int x = mouseEvent.getX();
|
int x = mouseEvent.getX();
|
||||||
int y = mouseEvent.getY();
|
int y = mouseEvent.getY();
|
||||||
@ -154,6 +182,10 @@ public class CraftingTable {
|
|||||||
Optional.of(items));
|
Optional.of(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfers all remaining items in the crafting grid back to the inventory
|
||||||
|
* and clears the crafting grid.
|
||||||
|
*/
|
||||||
public void exit() {
|
public void exit() {
|
||||||
// Put all items from crafting to inv
|
// Put all items from crafting to inv
|
||||||
for (int i = 0; i < items.length; i++) {
|
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) {
|
public CraftingTable(Game game) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ import cz.jzitnik.game.sprites.ui.Font.Size;
|
|||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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
|
@Slf4j
|
||||||
public class DeathScreen {
|
public class DeathScreen {
|
||||||
private int btnWidth;
|
private int btnWidth;
|
||||||
@ -21,6 +25,13 @@ public class DeathScreen {
|
|||||||
private int textButtonMargin;
|
private int textButtonMargin;
|
||||||
private boolean buttonHover;
|
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) {
|
public void render(StringBuilder buffer, Terminal terminal, Game game) {
|
||||||
log.debug("Rendering death screen");
|
log.debug("Rendering death screen");
|
||||||
var font = game.getGameStates().dependencies.font;
|
var font = game.getGameStates().dependencies.font;
|
||||||
@ -37,6 +48,16 @@ public class DeathScreen {
|
|||||||
renderButton(buffer, "Respawn", width, font, terminal, buttonHover);
|
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,
|
public void renderButton(StringBuilder buffer, String txt, int width, Font font, Terminal terminal,
|
||||||
boolean selected) {
|
boolean selected) {
|
||||||
btnWidth = Math.min(350, (int) (width * (3.0 / 4)));
|
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) {
|
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game) {
|
||||||
int x = mouseEvent.getX();
|
int x = mouseEvent.getX();
|
||||||
int y = mouseEvent.getY();
|
int y = mouseEvent.getY();
|
||||||
|
@ -7,28 +7,59 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
|||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import cz.jzitnik.tui.utils.Menu;
|
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 {
|
public class Escape {
|
||||||
private Game game;
|
private Game game;
|
||||||
private Menu menu;
|
private Menu menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the Escape menu for the given game instance.
|
||||||
|
*
|
||||||
|
* @param game The current game instance.
|
||||||
|
*/
|
||||||
public Escape(Game game) {
|
public Escape(Game game) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.menu = new Menu(game, "2DCraft", new String[] { "Continue", "Options", "Save and exit" },
|
this.menu = new Menu(game, "2DCraft", new String[] { "Continue", "Options", "Save and exit" },
|
||||||
this::onButtonClick);
|
this::onButtonClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the escape menu to its initial state.
|
||||||
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
menu.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) {
|
public void render(StringBuilder buffer, Terminal terminal) {
|
||||||
menu.render(buffer, 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) {
|
public void mouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||||
menu.handleMouse(mouseEvent, terminal, 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) {
|
private void onButtonClick(int index, ScreenRenderer screenRenderer) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -49,6 +80,12 @@ public class Escape {
|
|||||||
screenRenderer.render(game);
|
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) {
|
public void renderSave(StringBuilder buffer, Terminal terminal) {
|
||||||
var font = game.getGameStates().dependencies.font;
|
var font = game.getGameStates().dependencies.font;
|
||||||
var savingText = font.line(terminal, "Saving", Size.LARGE, Align.CENTER);
|
var savingText = font.line(terminal, "Saving", Size.LARGE, Align.CENTER);
|
||||||
@ -59,6 +96,12 @@ public class Escape {
|
|||||||
buffer.append(savingText.getData());
|
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) {
|
public void renderSaved(StringBuilder buffer, Terminal terminal) {
|
||||||
var font = game.getGameStates().dependencies.font;
|
var font = game.getGameStates().dependencies.font;
|
||||||
var savingText = font.line(terminal, "Saved", Size.LARGE, Align.CENTER);
|
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;
|
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 {
|
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) {
|
public static void render(StringBuilder buffer, SpriteList spriteList, Terminal terminal, Game game) {
|
||||||
int termWidth = terminal.getWidth();
|
int termWidth = terminal.getWidth();
|
||||||
int startLeft = Math.max(0, (termWidth / 2) - (INVENTORY_SIZE_PX / 2));
|
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.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a player's inventory including hotbar, crafting table, and item management.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class Inventory {
|
public class Inventory {
|
||||||
public static final int INVENTORY_SIZE_PX = 470;
|
public static final int INVENTORY_SIZE_PX = 470;
|
||||||
@ -32,6 +35,11 @@ public class Inventory {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean rightClick = false;
|
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() {
|
public Optional<Item> getItemInHand() {
|
||||||
if (hotbar[itemInhHandIndex] == null) {
|
if (hotbar[itemInhHandIndex] == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@ -39,6 +47,9 @@ public class Inventory {
|
|||||||
return Optional.of(hotbar[itemInhHandIndex].getItem().getLast());
|
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() {
|
public void decreaseItemInHand() {
|
||||||
if (hotbar[itemInhHandIndex] == null) {
|
if (hotbar[itemInhHandIndex] == null) {
|
||||||
return;
|
return;
|
||||||
@ -52,6 +63,13 @@ public class Inventory {
|
|||||||
hotbar[itemInhHandIndex].decrease();
|
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) {
|
private void placeItem(Item item) {
|
||||||
var inventoryItem = new InventoryItem(item);
|
var inventoryItem = new InventoryItem(item);
|
||||||
|
|
||||||
@ -74,18 +92,33 @@ public class Inventory {
|
|||||||
// If inventory is full the item is lost
|
// 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) {
|
public void addItem(InventoryItem item) {
|
||||||
for (int i = 0; i < item.getAmount(); i++) {
|
for (int i = 0; i < item.getAmount(); i++) {
|
||||||
addItem(item.getItem().get(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) {
|
public void addItem(List<Item> item) {
|
||||||
for (Item i : item) {
|
for (Item i : item) {
|
||||||
addItem(i);
|
addItem(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single Item to the inventory, stacking if possible.
|
||||||
|
*
|
||||||
|
* @param item Item to add.
|
||||||
|
*/
|
||||||
public void addItem(Item item) {
|
public void addItem(Item item) {
|
||||||
if (!item.isStackable()) {
|
if (!item.isStackable()) {
|
||||||
placeItem(item);
|
placeItem(item);
|
||||||
@ -113,6 +146,11 @@ public class Inventory {
|
|||||||
placeItem(item);
|
placeItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representing the background for the hotbar slots.
|
||||||
|
*
|
||||||
|
* @return Background sprite string.
|
||||||
|
*/
|
||||||
private String getHotbarBackground() {
|
private String getHotbarBackground() {
|
||||||
StringBuilder sprite = new StringBuilder();
|
StringBuilder sprite = new StringBuilder();
|
||||||
for (int i = 0; i < 25; i++) {
|
for (int i = 0; i < 25; i++) {
|
||||||
@ -122,6 +160,15 @@ public class Inventory {
|
|||||||
return sprite.toString();
|
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) {
|
public void renderHotbar(StringBuilder buffer, SpriteList spriteList, Terminal terminal, boolean isFull, Game game) {
|
||||||
int termWidth = terminal.getWidth();
|
int termWidth = terminal.getWidth();
|
||||||
int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2);
|
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,
|
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 widthPixels = COLUMN_AMOUNT * (50 + 4) + 2;
|
||||||
int heightPixels = ROW_AMOUNT * (25 + 1);
|
int heightPixels = ROW_AMOUNT * (25 + 1);
|
||||||
|
|
||||||
@ -214,6 +271,14 @@ public class Inventory {
|
|||||||
renderHotbar(buffer, spriteList, terminal, true, game);
|
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) {
|
public List<String> getSprites(InventoryItem[] items, SpriteList spriteList, int selectedItem, Game game) {
|
||||||
List<String> sprites = new ArrayList<>();
|
List<String> sprites = new ArrayList<>();
|
||||||
|
|
||||||
@ -255,16 +320,16 @@ public class Inventory {
|
|||||||
SpriteCombiner.combineTwoSprites(
|
SpriteCombiner.combineTwoSprites(
|
||||||
item.getItem().getFirst().getSpriteState().isPresent()
|
item.getItem().getFirst().getSpriteState().isPresent()
|
||||||
? spriteList.getSprite(item.getItem().getFirst().getSprite())
|
? spriteList.getSprite(item.getItem().getFirst().getSprite())
|
||||||
.getSprite(item.getItem().getFirst().getSpriteState()
|
.getSprite(item.getItem().getFirst().getSpriteState()
|
||||||
.get())
|
.get())
|
||||||
: spriteList.getSprite(item.getItem().getFirst().getSprite())
|
: spriteList.getSprite(item.getItem().getFirst().getSprite())
|
||||||
.getSprite(),
|
.getSprite(),
|
||||||
Numbers.getNumberSprite(item.getAmount(), game)));
|
Numbers.getNumberSprite(item.getAmount(), game)));
|
||||||
} else {
|
} else {
|
||||||
sprite = SpriteCombiner.combineTwoSprites(
|
sprite = SpriteCombiner.combineTwoSprites(
|
||||||
item.getItem().getFirst().getSpriteState().isPresent()
|
item.getItem().getFirst().getSpriteState().isPresent()
|
||||||
? spriteList.getSprite(item.getItem().getFirst().getSprite())
|
? 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(),
|
: spriteList.getSprite(item.getItem().getFirst().getSprite()).getSprite(),
|
||||||
Numbers.getNumberSprite(item.getAmount(), game));
|
Numbers.getNumberSprite(item.getAmount(), game));
|
||||||
}
|
}
|
||||||
@ -280,6 +345,12 @@ public class Inventory {
|
|||||||
return sprites;
|
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) {
|
public InventoryDTO getItem(int index, Optional<InventoryItem[]> i) {
|
||||||
if (index < 20) {
|
if (index < 20) {
|
||||||
// Normal inventory
|
// 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) {
|
public InventoryItem getSelectedItemNo(Optional<InventoryItem[]> i) {
|
||||||
InventoryDTO data = getItem(selectedItemInv, i);
|
InventoryDTO data = getItem(selectedItemInv, i);
|
||||||
if (selectedItemInv == -1) {
|
if (selectedItemInv == -1) {
|
||||||
|
@ -10,7 +10,21 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import static cz.jzitnik.game.ui.Inventory.*;
|
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 {
|
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,
|
public static void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game,
|
||||||
Optional<Integer> moveTopCustom, Optional<InventoryItem[]> i) {
|
Optional<Integer> moveTopCustom, Optional<InventoryItem[]> i) {
|
||||||
if (mouseEvent.getType() != MouseEvent.Type.Pressed)
|
if (mouseEvent.getType() != MouseEvent.Type.Pressed)
|
||||||
@ -98,6 +112,17 @@ public class InventoryClickHandler {
|
|||||||
screenRenderer.render(game);
|
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,
|
public static void handleItemClick(MouseEvent mouseEvent, Inventory inventory, Object[] items, int index,
|
||||||
int offset, Optional<InventoryItem[]> i) {
|
int offset, Optional<InventoryItem[]> i) {
|
||||||
int actualIndex = index + offset;
|
int actualIndex = index + offset;
|
||||||
|
@ -4,6 +4,9 @@ import cz.jzitnik.game.entities.items.InventoryItem;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Transfer Object for Inventory containing items and a selected index.
|
||||||
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public class InventoryDTO {
|
public class InventoryDTO {
|
||||||
|
@ -7,17 +7,34 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
|||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the options menu where the user can configure settings
|
||||||
|
* such as sound volume using a slider UI.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Options {
|
public class Options {
|
||||||
private Game game;
|
private Game game;
|
||||||
private int top;
|
private int top;
|
||||||
private int sliderWidth;
|
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) {
|
public Options(Game game) {
|
||||||
this.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) {
|
public void render(StringBuilder buffer, Terminal terminal) {
|
||||||
var buf = new StringBuilder();
|
var buf = new StringBuilder();
|
||||||
var font = game.getGameStates().dependencies.font;
|
var font = game.getGameStates().dependencies.font;
|
||||||
@ -40,6 +57,13 @@ public class Options {
|
|||||||
buffer.append(buf);
|
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) {
|
private void renderSlider(StringBuilder buffer, Terminal terminal, int percentage) {
|
||||||
var defaultColor = "\033[48;2;116;115;113m";
|
var defaultColor = "\033[48;2;116;115;113m";
|
||||||
var fullColor = "\033[48;2;70;70;70m";
|
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) {
|
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||||
int x = mouseEvent.getX();
|
int x = mouseEvent.getX();
|
||||||
int y = mouseEvent.getY() - top;
|
int y = mouseEvent.getY() - top;
|
||||||
|
@ -13,14 +13,30 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a small 2x2 crafting table that allows crafting of items
|
||||||
|
* based on defined recipes.
|
||||||
|
*/
|
||||||
public class SmallCraftingTable {
|
public class SmallCraftingTable {
|
||||||
|
|
||||||
|
/** Number of rows in the crafting grid. */
|
||||||
public static final int ROW_AMOUNT = 2;
|
public static final int ROW_AMOUNT = 2;
|
||||||
|
|
||||||
|
/** Number of columns in the crafting grid. */
|
||||||
public static final int COLUMN_AMOUNT = 2;
|
public static final int COLUMN_AMOUNT = 2;
|
||||||
|
|
||||||
|
/** The inventory associated with the crafting table. */
|
||||||
private Inventory inventory;
|
private Inventory inventory;
|
||||||
|
|
||||||
|
/** The items currently placed in the crafting grid. */
|
||||||
@Getter
|
@Getter
|
||||||
private InventoryItem[] items = new InventoryItem[4];
|
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() {
|
public void pickup() {
|
||||||
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
Optional<CraftingRecipe> recipe = CraftingRecipeList.getRecipe(Arrays.stream(items)
|
||||||
.map(item -> item == null ? null : item.getItem().getFirst().getId()).toArray(String[]::new));
|
.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) {
|
public StringBuilder render(SpriteList spriteList, Game game) {
|
||||||
var buf = new StringBuilder();
|
var buf = new StringBuilder();
|
||||||
|
|
||||||
@ -105,6 +128,11 @@ public class SmallCraftingTable {
|
|||||||
return buf;
|
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) {
|
public SmallCraftingTable(Inventory inventory) {
|
||||||
this.inventory = inventory;
|
this.inventory = inventory;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,56 @@
|
|||||||
package cz.jzitnik.game.ui;
|
package cz.jzitnik.game.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the different UI windows/screens available in the game.
|
||||||
|
*/
|
||||||
public enum Window {
|
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.List;
|
||||||
import java.util.Optional;
|
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
|
@AllArgsConstructor
|
||||||
public class MouseHandler {
|
public class MouseHandler {
|
||||||
private Game game;
|
private Game game;
|
||||||
private Terminal terminal;
|
private Terminal terminal;
|
||||||
private ScreenRenderer screenRenderer;
|
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) {
|
public void handle(MouseEvent mouseEvent) {
|
||||||
if (mouseEvent.getButton() == MouseEvent.Button.Button1 && mouseEvent.getType() == MouseEvent.Type.Pressed) {
|
if (mouseEvent.getButton() == MouseEvent.Button.Button1 && mouseEvent.getType() == MouseEvent.Type.Pressed) {
|
||||||
leftClick(mouseEvent);
|
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) {
|
private void scroll(MouseEvent mouseEvent) {
|
||||||
if (game.getWindow() == Window.WORLD) {
|
if (game.getWindow() == Window.WORLD) {
|
||||||
if (mouseEvent.getButton() == MouseEvent.Button.WheelUp) {
|
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) {
|
private void leftClick(MouseEvent mouseEvent) {
|
||||||
int mouseX = mouseEvent.getX();
|
int mouseX = mouseEvent.getX();
|
||||||
int mouseY = mouseEvent.getY();
|
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) {
|
private void rightClick(MouseEvent mouseEvent) {
|
||||||
int mouseX = mouseEvent.getX();
|
int mouseX = mouseEvent.getX();
|
||||||
int mouseY = mouseEvent.getY();
|
int mouseY = mouseEvent.getY();
|
||||||
@ -110,6 +136,12 @@ public class MouseHandler {
|
|||||||
game.build(blockX, blockY, screenRenderer);
|
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) {
|
private void move(MouseEvent mouseEvent) {
|
||||||
if (game.isMining() || game.getWindow() != Window.WORLD) {
|
if (game.isMining() || game.getWindow() != Window.WORLD) {
|
||||||
return;
|
return;
|
||||||
|
@ -6,8 +6,17 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class responsible for loading text-based resources (e.g., ANSI art) from the classpath.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ResourceLoader {
|
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) {
|
public static String loadResource(String fileName) {
|
||||||
log.info("Loading resource: {}", "textures/" + fileName);
|
log.info("Loading resource: {}", "textures/" + fileName);
|
||||||
try (InputStream inputStream = ResourceLoader.class.getClassLoader()
|
try (InputStream inputStream = ResourceLoader.class.getClassLoader()
|
||||||
|
@ -1,6 +1,23 @@
|
|||||||
package cz.jzitnik.tui;
|
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 {
|
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) {
|
public static int[] calculate(int x, int y, int terminalHeight, int terminalWidth, int worldX, int worldY) {
|
||||||
int spriteWidth = 50;
|
int spriteWidth = 50;
|
||||||
int spriteHeight = 25;
|
int spriteHeight = 25;
|
||||||
|
@ -19,20 +19,39 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
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
|
@Slf4j
|
||||||
public class ScreenRenderer {
|
public class ScreenRenderer {
|
||||||
private final SpriteList spriteList;
|
private final SpriteList spriteList;
|
||||||
private final Terminal terminal;
|
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) {
|
public ScreenRenderer(SpriteList spriteList, Terminal terminal) {
|
||||||
this.spriteList = spriteList;
|
this.spriteList = spriteList;
|
||||||
this.terminal = terminal;
|
this.terminal = terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional coordinates of the currently selected block. Used for visual highlighting.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private Optional<List<Integer>> selectedBlock = Optional.empty();
|
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) {
|
private int[] getPlayerCords(List<Block>[][] world) {
|
||||||
for (int i = 0; i < world.length; i++) {
|
for (int i = 0; i < world.length; i++) {
|
||||||
for (int j = 0; j < world[i].length; j++) {
|
for (int j = 0; j < world[i].length; j++) {
|
||||||
@ -51,6 +70,14 @@ public class ScreenRenderer {
|
|||||||
return null;
|
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) {
|
public String getTexture(Block block, SpriteList spriteList, Game game) {
|
||||||
if (Air.class.isAssignableFrom(spriteList.getSprite(block.getSprite()).getClass())) {
|
if (Air.class.isAssignableFrom(spriteList.getSprite(block.getSprite()).getClass())) {
|
||||||
var air = (Air) spriteList.getSprite(block.getSprite());
|
var air = (Air) spriteList.getSprite(block.getSprite());
|
||||||
@ -64,6 +91,12 @@ public class ScreenRenderer {
|
|||||||
return spriteList.getSprite(block.getSprite()).getSprite(block.getSpriteState().get());
|
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) {
|
public synchronized void render(Game game) {
|
||||||
log.debug("Rendering frame");
|
log.debug("Rendering frame");
|
||||||
var world = game.getWorld();
|
var world = game.getWorld();
|
||||||
@ -185,6 +218,11 @@ public class ScreenRenderer {
|
|||||||
System.out.println(main);
|
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() {
|
private static StringBuilder getStringBuilder() {
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
@ -5,8 +5,35 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import javax.sound.sampled.*;
|
import javax.sound.sampled.*;
|
||||||
import java.io.IOException;
|
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
|
@Slf4j
|
||||||
public class SoundPlayer {
|
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)
|
public static void playSound(String filePath, int backendVolume, int masterVolume)
|
||||||
throws LineUnavailableException, IOException, UnsupportedAudioFileException {
|
throws LineUnavailableException, IOException, UnsupportedAudioFileException {
|
||||||
if (!filePath.endsWith(".ogg") || masterVolume == 0) {
|
if (!filePath.endsWith(".ogg") || masterVolume == 0) {
|
||||||
@ -54,7 +81,19 @@ public class SoundPlayer {
|
|||||||
dataIn.close();
|
dataIn.close();
|
||||||
audioStream.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) {
|
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
|
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);
|
int sample = (buffer[i] & 0xFF) | (buffer[i + 1] << 8);
|
||||||
|
@ -3,6 +3,12 @@ package cz.jzitnik.tui;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Optional;
|
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>> {
|
public abstract class Sprite<E extends Enum<E>> {
|
||||||
private HashMap<E, String> resourcesLocation;
|
private HashMap<E, String> resourcesLocation;
|
||||||
private HashMap<E, Optional<String>> resources;
|
private HashMap<E, Optional<String>> resources;
|
||||||
@ -10,6 +16,12 @@ public abstract class Sprite<E extends Enum<E>> {
|
|||||||
private String defaultResourceLocation;
|
private String defaultResourceLocation;
|
||||||
private Class<E> enumClass;
|
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) {
|
protected final void loadResources(HashMap<E, String> values, Class<E> enumClass) {
|
||||||
resources = new HashMap<>();
|
resources = new HashMap<>();
|
||||||
resourcesLocation = new HashMap<>();
|
resourcesLocation = new HashMap<>();
|
||||||
@ -22,6 +34,12 @@ public abstract class Sprite<E extends Enum<E>> {
|
|||||||
this.enumClass = enumClass;
|
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) {
|
private String loadResource(E key) {
|
||||||
var location = resourcesLocation.get(key);
|
var location = resourcesLocation.get(key);
|
||||||
var resource = ResourceLoader.loadResource(location);
|
var resource = ResourceLoader.loadResource(location);
|
||||||
@ -31,12 +49,21 @@ public abstract class Sprite<E extends Enum<E>> {
|
|||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sets the location for the default sprite resource.
|
||||||
|
*
|
||||||
|
* @param resource Path to the default resource.
|
||||||
|
*/
|
||||||
protected final void loadResource(String resource) {
|
protected final void loadResource(String resource) {
|
||||||
defaultResourceLocation = 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);
|
var resource = ResourceLoader.loadResource(defaultResourceLocation);
|
||||||
|
|
||||||
defaultResource = resource;
|
defaultResource = resource;
|
||||||
@ -44,6 +71,13 @@ public abstract class Sprite<E extends Enum<E>> {
|
|||||||
return resource;
|
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) {
|
protected final String getResource(E key) {
|
||||||
var resource = resources.get(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() {
|
protected final String getResource() {
|
||||||
if (defaultResource != null) {
|
if (defaultResource != null) {
|
||||||
return defaultResource;
|
return defaultResource;
|
||||||
@ -61,11 +101,27 @@ public abstract class Sprite<E extends Enum<E>> {
|
|||||||
return loadResource();
|
return loadResource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns the sprite for the default state.
|
||||||
|
*
|
||||||
|
* @return String representing the sprite.
|
||||||
|
*/
|
||||||
public abstract String getSprite();
|
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);
|
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() {
|
public Optional<Class<E>> getStates() {
|
||||||
return Optional.ofNullable(enumClass);
|
return Optional.ofNullable(enumClass);
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,22 @@ package cz.jzitnik.tui;
|
|||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
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>> {
|
public class SpriteList<E extends Enum<E>> {
|
||||||
private final EnumMap<E, Sprite> sprites;
|
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) {
|
public SpriteList(Class<E> enumClass, HashMap<E, Sprite> initialMap) {
|
||||||
sprites = new EnumMap<>(enumClass);
|
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) {
|
public Sprite getSprite(E key) {
|
||||||
return sprites.get(key);
|
return sprites.get(key);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,12 @@ import cz.jzitnik.game.sprites.ui.Font.*;
|
|||||||
import cz.jzitnik.tui.ScreenRenderer;
|
import cz.jzitnik.tui.ScreenRenderer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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
|
@Slf4j
|
||||||
public class Menu {
|
public class Menu {
|
||||||
private Game game;
|
private Game game;
|
||||||
@ -23,6 +29,14 @@ public class Menu {
|
|||||||
private int textButtonMargin;
|
private int textButtonMargin;
|
||||||
private ButtonClickHandler buttonClickHandler;
|
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) {
|
public Menu(Game game, String headingText, String[] buttonLabels, ButtonClickHandler buttonClickHandler) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.headingText = headingText;
|
this.headingText = headingText;
|
||||||
@ -31,24 +45,38 @@ public class Menu {
|
|||||||
this.buttonClickHandler = buttonClickHandler;
|
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) {
|
public void render(StringBuilder buffer, Terminal terminal) {
|
||||||
var font = game.getGameStates().dependencies.font;
|
var font = game.getGameStates().dependencies.font;
|
||||||
var width = terminal.getWidth();
|
var width = terminal.getWidth();
|
||||||
var height = terminal.getHeight();
|
var height = terminal.getHeight();
|
||||||
|
|
||||||
var heading = font.line(terminal, headingText, Size.LARGE, Align.CENTER);
|
var heading = font.line(terminal, headingText, Size.LARGE, Align.CENTER);
|
||||||
buffer.append(heading.getData());
|
buffer.append(heading.getData());
|
||||||
mainTextHeight = heading.getHeight();
|
mainTextHeight = heading.getHeight();
|
||||||
|
|
||||||
textButtonMargin = (height < 600) ? 15 : (height < 800) ? 30 : 50;
|
textButtonMargin = (height < 600) ? 15 : (height < 800) ? 30 : 50;
|
||||||
buffer.append("\n".repeat(textButtonMargin));
|
buffer.append("\n".repeat(textButtonMargin));
|
||||||
|
|
||||||
for (int i = 0; i < buttonLabels.length; i++) {
|
for (int i = 0; i < buttonLabels.length; i++) {
|
||||||
renderButton(buffer, buttonLabels[i], width, font, terminal, buttonsHover[i]);
|
renderButton(buffer, buttonLabels[i], width, font, terminal, buttonsHover[i]);
|
||||||
buffer.append("\n".repeat(textButtonMargin / 2));
|
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) {
|
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||||
int x = mouseEvent.getX();
|
int x = mouseEvent.getX();
|
||||||
int y = mouseEvent.getY();
|
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) {
|
public void renderButton(StringBuilder buffer, String txt, int width, Font font, Terminal terminal, boolean selected) {
|
||||||
btnWidth = Math.min(350, (int) (width * (3.0 / 4)));
|
btnWidth = Math.min(350, (int) (width * (3.0 / 4)));
|
||||||
leftPad = (width - btnWidth) / 2;
|
leftPad = (width - btnWidth) / 2;
|
||||||
@ -109,13 +147,23 @@ public class Menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for handling button click actions.
|
||||||
|
*/
|
||||||
public interface ButtonClickHandler {
|
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);
|
void onClick(int index, ScreenRenderer screenRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all hover states on the buttons (useful when switching views).
|
||||||
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
for (int i = 0; i < buttonsHover.length; i++) {
|
Arrays.fill(buttonsHover, false);
|
||||||
buttonsHover[i] = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,20 @@ package cz.jzitnik.tui.utils;
|
|||||||
|
|
||||||
import cz.jzitnik.game.Game;
|
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 {
|
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) {
|
public static String fixForHotbar(String str, int height, int width) {
|
||||||
var parts = str.split("\n");
|
var parts = str.split("\n");
|
||||||
// Make it 50x25
|
// Make it 50x25
|
||||||
@ -24,6 +37,14 @@ public class Numbers {
|
|||||||
return stringBuilder.toString();
|
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) {
|
public static String getNumberSprite(int number, Game game) {
|
||||||
if (number == 1) {
|
if (number == 1) {
|
||||||
StringBuilder sprite = new StringBuilder();
|
StringBuilder sprite = new StringBuilder();
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
package cz.jzitnik.tui.utils;
|
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 {
|
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) {
|
public static String combineSprites(String[] sprites) {
|
||||||
if (sprites == null || sprites.length == 0) {
|
if (sprites == null || sprites.length == 0) {
|
||||||
return "";
|
return "";
|
||||||
@ -15,6 +27,14 @@ public class SpriteCombiner {
|
|||||||
return combinedSprite;
|
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) {
|
public static String combineTwoSprites(String sprite1, String sprite2) {
|
||||||
String[] rows1 = sprite1.split("\n");
|
String[] rows1 = sprite1.split("\n");
|
||||||
String[] rows2 = sprite2.split("\n");
|
String[] rows2 = sprite2.split("\n");
|
||||||
@ -69,6 +89,13 @@ public class SpriteCombiner {
|
|||||||
return combinedSprite.toString();
|
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) {
|
private static String getColorCode(String row, int index) {
|
||||||
if (row.charAt(index) != '\033') {
|
if (row.charAt(index) != '\033') {
|
||||||
return getColorCode(row, index - 1);
|
return getColorCode(row, index - 1);
|
||||||
@ -77,6 +104,13 @@ public class SpriteCombiner {
|
|||||||
return extractColorCode(row, index);
|
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) {
|
private static String extractColorCode(String row, int index) {
|
||||||
StringBuilder colorCode = new StringBuilder();
|
StringBuilder colorCode = new StringBuilder();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user