chore: Idk some changes that I made
This commit is contained in:
@ -34,8 +34,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Main class representing the game world and core logic.
|
||||
* <p>
|
||||
* Manages the game state, player actions, world updates and interactions.
|
||||
*/
|
||||
@Getter
|
||||
public class Game {
|
||||
/**
|
||||
* The 2D world grid consisting of block lists for each coordinate.
|
||||
* Dimensions: [height][width] = [256][512].
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private final List<Block>[][] world = (List<Block>[][]) new CopyOnWriteArrayList[256][512];
|
||||
private final Player player = new Player();
|
||||
@ -45,15 +54,25 @@ public class Game {
|
||||
private final Inventory inventory = new Inventory();
|
||||
private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
||||
private transient GameStates gameStates = new GameStates(this);
|
||||
|
||||
/** Current time of day in the game (0–600 range). */
|
||||
@Setter
|
||||
private int daytime = 0; // 0-600
|
||||
private int daytime = 0;
|
||||
//
|
||||
private Configuration configuration = new Configuration();
|
||||
|
||||
/**
|
||||
* Constructs the game instance and generates the initial world.
|
||||
*/
|
||||
public Game() {
|
||||
Generation.generateWorld(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current coordinates of the player (bottom half).
|
||||
*
|
||||
* @return int array with x and y coordinates, or null if not found.
|
||||
*/
|
||||
public int[] getPlayerCords() {
|
||||
for (int i = 0; i < world.length; i++) {
|
||||
for (int j = 0; j < world[i].length; j++) {
|
||||
@ -73,6 +92,12 @@ public class Game {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the player one block to the right if possible.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
* @param terminal the terminal instance used for layout.
|
||||
*/
|
||||
public void movePlayerRight(ScreenRenderer screenRenderer, Terminal terminal) {
|
||||
if (window != Window.WORLD) {
|
||||
return;
|
||||
@ -96,6 +121,12 @@ public class Game {
|
||||
update(screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the player one block to the left if possible.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
* @param terminal the terminal instance used for layout.
|
||||
*/
|
||||
public void movePlayerLeft(ScreenRenderer screenRenderer, Terminal terminal) {
|
||||
if (window != Window.WORLD) {
|
||||
return;
|
||||
@ -119,6 +150,11 @@ public class Game {
|
||||
update(screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the player upward by one block, if jumping is valid.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
*/
|
||||
public void movePlayerUp(ScreenRenderer screenRenderer) {
|
||||
if (window != Window.WORLD) {
|
||||
return;
|
||||
@ -145,6 +181,13 @@ public class Game {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attacks mobs at a specified location.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
*/
|
||||
public void hit(ScreenRenderer screenRenderer, int x, int y) {
|
||||
if (mining || window != Window.WORLD) {
|
||||
return;
|
||||
@ -182,6 +225,13 @@ public class Game {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the mining process at a given location.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
*/
|
||||
public void mine(ScreenRenderer screenRenderer, int x, int y) {
|
||||
if (mining || window != Window.WORLD) {
|
||||
return;
|
||||
@ -250,6 +300,14 @@ public class Game {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantly mines a block without animation delay.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
* @param minedDirectly whether the block was mined directly by player.
|
||||
*/
|
||||
public void mineInstant(ScreenRenderer screenRenderer, int x, int y, boolean minedDirectly) {
|
||||
var blocks = world[y][x];
|
||||
var blocksCopy = new ArrayList<>(blocks);
|
||||
@ -316,6 +374,14 @@ public class Game {
|
||||
update(screenRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a block is valid for mining based on proximity and visibility.
|
||||
*
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
* @param terminal terminal context used to determine screen bounds.
|
||||
* @return true if mineable, false otherwise.
|
||||
*/
|
||||
public boolean isMineable(int x, int y, Terminal terminal) {
|
||||
List<Block> blocks = world[y][x];
|
||||
|
||||
@ -340,6 +406,15 @@ public class Game {
|
||||
&& blocks.stream().anyMatch(Block::isMineable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a block is valid for hitting (attacking).
|
||||
*
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
* @param terminal terminal context used to determine screen bounds.
|
||||
* @return true if a mob can be hit at the given location.
|
||||
*/
|
||||
public boolean isHitable(int x, int y, Terminal terminal) {
|
||||
List<Block> blocks = world[y][x];
|
||||
|
||||
@ -364,6 +439,11 @@ public class Game {
|
||||
&& blocks.stream().anyMatch(Block::isMob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodically checks and updates the player’s falling state due to gravity.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
*/
|
||||
public void update(ScreenRenderer screenRenderer) {
|
||||
while (true) {
|
||||
try {
|
||||
@ -395,6 +475,14 @@ public class Game {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to place a block or interact with the environment at a given location.
|
||||
* Also handles eating and tool usage.
|
||||
*
|
||||
* @param x x-coordinate.
|
||||
* @param y y-coordinate.
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
*/
|
||||
public void build(int x, int y, ScreenRenderer screenRenderer) {
|
||||
if (window != Window.WORLD) {
|
||||
return;
|
||||
@ -476,6 +564,12 @@ public class Game {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the selected inventory slot (hotbar).
|
||||
*
|
||||
* @param slot the slot index to switch to.
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
*/
|
||||
public void changeSlot(int slot, ScreenRenderer screenRenderer) {
|
||||
if (window != Window.WORLD) {
|
||||
return;
|
||||
@ -485,10 +579,21 @@ public class Game {
|
||||
screenRenderer.render(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block list represents a solid space (i.e., not all ghost blocks).
|
||||
*
|
||||
* @param blocks list of blocks at a coordinate.
|
||||
* @return true if any block is not a ghost block.
|
||||
*/
|
||||
public boolean isSolid(List<Block> blocks) {
|
||||
return !blocks.stream().allMatch(Block::isGhost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visually and logically triggers the player "hit" (damage) animation.
|
||||
*
|
||||
* @param screenRenderer the renderer used to refresh the screen.
|
||||
*/
|
||||
public void playerHit(ScreenRenderer screenRenderer) {
|
||||
player.getPlayerBlock1().setSpriteState(SteveState.FIRST_HURT);
|
||||
player.getPlayerBlock2().setSpriteState(SteveState.SECOND_HURT);
|
||||
|
@ -76,6 +76,7 @@ public class SpriteLoader {
|
||||
PIG,
|
||||
SHEEP,
|
||||
COW,
|
||||
ZOMBIE,
|
||||
|
||||
// UI
|
||||
BREAKING,
|
||||
@ -267,6 +268,7 @@ public class SpriteLoader {
|
||||
SPRITES_MAP.put(SPRITES.PIG, new Pig());
|
||||
SPRITES_MAP.put(SPRITES.SHEEP, new Sheep());
|
||||
SPRITES_MAP.put(SPRITES.COW, new Cow());
|
||||
SPRITES_MAP.put(SPRITES.ZOMBIE, new Zombie());
|
||||
|
||||
// UI
|
||||
SPRITES_MAP.put(SPRITES.BREAKING, new Breaking());
|
||||
|
@ -3,7 +3,7 @@ package cz.jzitnik.game.core.sound.registry;
|
||||
import cz.jzitnik.game.annotations.SoundRegistry;
|
||||
import cz.jzitnik.game.core.sound.SoundKey;
|
||||
|
||||
@SoundRegistry(key = SoundKey.STONE_WALKING, resourceLocation = {
|
||||
@SoundRegistry(key = SoundKey.WOOD_WALKING, resourceLocation = {
|
||||
"wood/step1.ogg",
|
||||
"wood/step2.ogg",
|
||||
"wood/step3.ogg",
|
||||
|
@ -11,6 +11,7 @@ import cz.jzitnik.game.mobs.EntityHurtAnimation;
|
||||
import cz.jzitnik.game.mobs.EntityKill;
|
||||
import cz.jzitnik.game.smelting.Smelting;
|
||||
import cz.jzitnik.game.sprites.ui.Font;
|
||||
import cz.jzitnik.game.ui.DeathScreen;
|
||||
import cz.jzitnik.game.ui.Escape;
|
||||
import cz.jzitnik.game.ui.Options;
|
||||
|
||||
@ -27,6 +28,7 @@ public class Dependencies {
|
||||
public Font font = new Font();
|
||||
public Escape escape;
|
||||
public Options options;
|
||||
public DeathScreen deathScreen = new DeathScreen();
|
||||
|
||||
public Dependencies(Game game) {
|
||||
escape = new Escape(game);
|
||||
|
@ -10,6 +10,7 @@ import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.annotations.ReduceFallDamage;
|
||||
import cz.jzitnik.game.core.reducefalldamage.Reducer;
|
||||
import cz.jzitnik.game.core.sound.SoundKey;
|
||||
import cz.jzitnik.game.ui.Window;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
|
||||
@Getter
|
||||
@ -41,18 +42,19 @@ public class Player {
|
||||
}
|
||||
|
||||
public void fell(List<Block> fallblock, Game game, ScreenRenderer screenRenderer) {
|
||||
var block = fallblock.stream().filter(b -> b.getClass().isAnnotationPresent(ReduceFallDamage.class)).findFirst();
|
||||
var block = fallblock.stream().filter(b -> b.getClass().isAnnotationPresent(ReduceFallDamage.class))
|
||||
.findFirst();
|
||||
int damage = Math.max(fallDistance - 3, 0);
|
||||
if (block.isPresent()) {
|
||||
var reducerClass = block.get().getClass().getAnnotation(ReduceFallDamage.class).value();
|
||||
try {
|
||||
Reducer reducer = reducerClass.getDeclaredConstructor().newInstance();
|
||||
Reducer reducer = reducerClass.getDeclaredConstructor().newInstance();
|
||||
damage = reducer.reduce(damage);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
dealDamage(damage, game, screenRenderer);
|
||||
fallDistance = 0;
|
||||
@ -65,8 +67,9 @@ public class Player {
|
||||
game.getGameStates().dependencies.sound.playSound(game.getConfiguration(), SoundKey.HURT, null);
|
||||
}
|
||||
|
||||
if (health == 0) {
|
||||
// TODO: Implement dead
|
||||
if (health <= 0) {
|
||||
game.setWindow(Window.DEATH_SCREEN);
|
||||
screenRenderer.render(game);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package cz.jzitnik.game.entities.items.registry.mobs;
|
||||
|
||||
import cz.jzitnik.game.SpriteLoader;
|
||||
import cz.jzitnik.game.annotations.EntityRegistry;
|
||||
import cz.jzitnik.game.entities.Block;
|
||||
import cz.jzitnik.game.mobs.services.zombie.ZombieData;
|
||||
|
||||
@EntityRegistry("zombie")
|
||||
public class Zombie extends Block {
|
||||
public Zombie() {
|
||||
super("zombie", SpriteLoader.SPRITES.ZOMBIE);
|
||||
setMob(true);
|
||||
setGhost(true);
|
||||
setSpriteState(cz.jzitnik.game.sprites.Zombie.ZombieState.TOP);
|
||||
setMineable(false);
|
||||
setData(new ZombieData());
|
||||
setHp(10);
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package cz.jzitnik.game.mobs.services.sheep;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class SheepData {
|
||||
private int lastDirection = 1; // 1 = right, -1 = left
|
||||
private int movementCooldown = 0;
|
||||
|
@ -0,0 +1,12 @@
|
||||
package cz.jzitnik.game.mobs.services.zombie;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ZombieData {
|
||||
private int lastDirection = 1; // 1 = right, -1 = left
|
||||
private int movementCooldown = 0;
|
||||
private int jumpAttempts = 0;
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package cz.jzitnik.game.mobs.services.zombie;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.annotations.EntityHurtAnimationHandler;
|
||||
import cz.jzitnik.game.annotations.EntityKillHandler;
|
||||
import cz.jzitnik.game.annotations.EntityLogic;
|
||||
import cz.jzitnik.game.annotations.EntitySpawn;
|
||||
import cz.jzitnik.game.entities.Block;
|
||||
import cz.jzitnik.game.entities.items.InventoryItem;
|
||||
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
|
||||
import cz.jzitnik.game.mobs.*;
|
||||
import cz.jzitnik.game.sprites.Zombie;
|
||||
import cz.jzitnik.game.sprites.Zombie.ZombieState;
|
||||
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cz.jzitnik.game.sprites.Zombie.ZombieState.*;
|
||||
|
||||
@EntitySpawn
|
||||
@EntityLogic("zombie")
|
||||
@EntityHurtAnimationHandler("zombie")
|
||||
@EntityKillHandler("zombie")
|
||||
public class ZombieLogic
|
||||
implements EntityLogicInterface, EntitySpawnInterface, EntityHurtAnimationChanger, EntityKillInterface {
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(int playerX, int playerY, Game game, Terminal terminal) {
|
||||
int[] view = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length);
|
||||
var world = game.getWorld();
|
||||
int startX = view[0];
|
||||
int endX = view[1];
|
||||
|
||||
attemptZombieSpawn(startX - 20, startX - 5, playerY, game);
|
||||
attemptZombieSpawn(endX + 5, endX + 20, playerY, game);
|
||||
}
|
||||
|
||||
private void attemptZombieSpawn(int startX, int endX, int centerY, Game game) {
|
||||
if (countZombies(startX, endX, centerY - 15, centerY + 15, game) < 3 && random.nextInt(100) < 2) {
|
||||
var spawnLocations = zombieCanSpawn(startX, endX, centerY, game);
|
||||
if (!spawnLocations.isEmpty()) {
|
||||
for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
|
||||
var loc = getRandomEntry(spawnLocations);
|
||||
int x = loc.getKey();
|
||||
int y = loc.getValue();
|
||||
|
||||
var top = ItemBlockSupplier.getEntity("zombie");
|
||||
top.setSpriteState(ZombieState.TOP);
|
||||
|
||||
var bottom = ItemBlockSupplier.getEntity("zombie");
|
||||
bottom.setSpriteState(ZombieState.BOTTOM);
|
||||
|
||||
game.getWorld()[y - 1][x].add(top);
|
||||
game.getWorld()[y][x].add(bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HashMap<Integer, Integer> zombieCanSpawn(int startX, int endX, int playerY, Game game) {
|
||||
var map = new HashMap<Integer, Integer>();
|
||||
var world = game.getWorld();
|
||||
for (int x = startX; x <= endX; x++) {
|
||||
for (int y = Math.max(1, playerY - 30); y < Math.min(world.length - 2, playerY + 30); y++) {
|
||||
if (world[y + 1][x].stream().anyMatch(b -> b.getBlockId().equals("grass"))) {
|
||||
map.put(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private long countZombies(int startX, int endX, int startY, int endY, Game game) {
|
||||
long count = 0;
|
||||
for (int y = startY; y <= endY; y++) {
|
||||
for (int x = startX; x <= endX; x++) {
|
||||
count += game.getWorld()[y][x].stream().filter(i -> i.getBlockId().equals("zombie")).count();
|
||||
}
|
||||
}
|
||||
return count / 2; // Each zombie has two parts
|
||||
}
|
||||
|
||||
public Zombie.ZombieState setHurtAnimation(boolean hurt, Enum current) {
|
||||
if (hurt) {
|
||||
return switch (current) {
|
||||
case TOP, TOP_HURT -> TOP_HURT;
|
||||
case BOTTOM, BOTTOM_HURT -> BOTTOM_HURT;
|
||||
default -> throw new IllegalStateException("Unexpected state: " + current);
|
||||
};
|
||||
}
|
||||
|
||||
return switch (current) {
|
||||
case TOP, TOP_HURT -> TOP;
|
||||
case BOTTOM, BOTTOM_HURT -> BOTTOM;
|
||||
default -> throw new IllegalStateException("Unexpected state: " + current);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(Game game, Block mob) {
|
||||
int rottenFlesh = random.nextInt(3) + 1;
|
||||
InventoryItem drop = new InventoryItem(rottenFlesh, ItemBlockSupplier.getItem("rotten_flesh"));
|
||||
game.getInventory().addItem(drop);
|
||||
}
|
||||
|
||||
public static <K, V> Map.Entry<K, V> getRandomEntry(HashMap<K, V> map) {
|
||||
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
|
||||
return list.get(new Random().nextInt(list.size()));
|
||||
}
|
||||
}
|
33
src/main/java/cz/jzitnik/game/sprites/Zombie.java
Normal file
33
src/main/java/cz/jzitnik/game/sprites/Zombie.java
Normal file
@ -0,0 +1,33 @@
|
||||
package cz.jzitnik.game.sprites;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import cz.jzitnik.tui.Sprite;
|
||||
|
||||
public class Zombie extends Sprite<Zombie.ZombieState> {
|
||||
public enum ZombieState {
|
||||
TOP, TOP_HURT,
|
||||
BOTTOM, BOTTOM_HURT
|
||||
}
|
||||
|
||||
public Zombie() {
|
||||
loadResources(new HashMap<>() {
|
||||
{
|
||||
put(ZombieState.TOP, "mobs/zombie/top.ans");
|
||||
put(ZombieState.TOP_HURT, "mobs/zombie/tophurt.ans");
|
||||
put(ZombieState.BOTTOM, "mobs/zombie/bottom.ans");
|
||||
put(ZombieState.BOTTOM_HURT, "mobs/zombie/bottomhurt.ans");
|
||||
}
|
||||
}, ZombieState.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSprite() {
|
||||
return getSprite(ZombieState.TOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSprite(ZombieState key) {
|
||||
return super.getResource(key);
|
||||
}
|
||||
}
|
@ -235,7 +235,6 @@ public class Font {
|
||||
|
||||
public FontDTO line(Terminal terminal, String text, Object... attributes) {
|
||||
int termWidth = terminal.getWidth();
|
||||
// int termHeight = terminal.getHeight();
|
||||
|
||||
Size fontSize = Size.MEDIUM;
|
||||
Align alignment = Align.LEFT;
|
||||
|
@ -19,20 +19,18 @@ public class InputHandlerThread extends Thread {
|
||||
private final Terminal terminal;
|
||||
private final ScreenRenderer screenRenderer;
|
||||
private final MouseHandler mouseHandler;
|
||||
private final boolean[] isRunning;
|
||||
|
||||
public InputHandlerThread(Game game, Terminal terminal, ScreenRenderer screenRenderer, boolean[] isRunning) {
|
||||
public InputHandlerThread(Game game, Terminal terminal, ScreenRenderer screenRenderer) {
|
||||
this.game = game;
|
||||
this.terminal = terminal;
|
||||
this.screenRenderer = screenRenderer;
|
||||
this.mouseHandler = new MouseHandler(game, terminal, screenRenderer);
|
||||
this.isRunning = isRunning;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (isRunning[0]) {
|
||||
while (true) {
|
||||
int key = terminal.reader().read();
|
||||
|
||||
if (key == 27) { // ESC
|
||||
@ -46,19 +44,25 @@ public class InputHandlerThread extends Thread {
|
||||
case INVENTORY -> InventoryClickHandler.click(mouseEvent, terminal, screenRenderer,
|
||||
game, Optional.empty(), Optional.empty());
|
||||
case CRAFTING_TABLE ->
|
||||
game.getGameStates().craftingTable.click(mouseEvent, terminal, screenRenderer);
|
||||
game.getGameStates().craftingTable.click(mouseEvent, terminal, screenRenderer);
|
||||
case CHEST -> ((Chest) game.getWorld()[game.getGameStates().clickY][game
|
||||
.getGameStates().clickX].stream().filter(i -> i.getBlockId().equals("chest"))
|
||||
.toList().getFirst().getData()).click(game, mouseEvent, terminal,
|
||||
screenRenderer);
|
||||
.toList().getFirst().getData()).click(game, mouseEvent, terminal,
|
||||
screenRenderer);
|
||||
case FURNACE -> ((Furnace) game.getWorld()[game.getGameStates().clickY][game
|
||||
.getGameStates().clickX].stream().filter(i -> i.getBlockId().equals("furnace"))
|
||||
.toList().getFirst().getData()).click(game, mouseEvent, terminal,
|
||||
screenRenderer);
|
||||
case ESC -> game.getGameStates().dependencies.escape.mouse(mouseEvent, terminal, screenRenderer);
|
||||
case SAVE_EXIT -> {}
|
||||
case OPTIONS -> game.getGameStates().dependencies.options.handleMouse(mouseEvent, terminal, screenRenderer);
|
||||
case SAVED -> {}
|
||||
.toList().getFirst().getData()).click(game, mouseEvent, terminal,
|
||||
screenRenderer);
|
||||
case ESC -> game.getGameStates().dependencies.escape.mouse(mouseEvent, terminal,
|
||||
screenRenderer);
|
||||
case SAVE_EXIT -> {
|
||||
}
|
||||
case OPTIONS -> game.getGameStates().dependencies.options.handleMouse(mouseEvent,
|
||||
terminal, screenRenderer);
|
||||
case DEATH_SCREEN -> game.getGameStates().dependencies.deathScreen
|
||||
.handleMouse(mouseEvent, terminal, screenRenderer, game);
|
||||
case SAVED -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
99
src/main/java/cz/jzitnik/game/ui/DeathScreen.java
Normal file
99
src/main/java/cz/jzitnik/game/ui/DeathScreen.java
Normal file
@ -0,0 +1,99 @@
|
||||
package cz.jzitnik.game.ui;
|
||||
|
||||
import org.jline.terminal.MouseEvent;
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.sprites.ui.Font;
|
||||
import cz.jzitnik.game.sprites.ui.Font.Align;
|
||||
import cz.jzitnik.game.sprites.ui.Font.Background;
|
||||
import cz.jzitnik.game.sprites.ui.Font.Custom;
|
||||
import cz.jzitnik.game.sprites.ui.Font.Size;
|
||||
import cz.jzitnik.tui.ScreenRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class DeathScreen {
|
||||
private int btnWidth;
|
||||
private int leftPad;
|
||||
private int mainTextHeight;
|
||||
private int buttonHeight;
|
||||
private int textButtonMargin;
|
||||
private boolean buttonHover;
|
||||
|
||||
public void render(StringBuilder buffer, Terminal terminal, Game game) {
|
||||
log.debug("Rendering death screen");
|
||||
var font = game.getGameStates().dependencies.font;
|
||||
var width = terminal.getWidth();
|
||||
var height = terminal.getHeight();
|
||||
|
||||
var heading = font.line(terminal, "You Died", Size.LARGE, Align.CENTER);
|
||||
|
||||
buffer.append(heading.getData());
|
||||
|
||||
textButtonMargin = (height < 600) ? 15 : (height < 800) ? 30 : 50;
|
||||
buffer.append("\n".repeat(textButtonMargin));
|
||||
|
||||
renderButton(buffer, "Respawn", width, font, terminal, buttonHover);
|
||||
}
|
||||
|
||||
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;
|
||||
log.debug("Button width: {}px ", btnWidth);
|
||||
|
||||
final String color = selected ? "[48;2;70;70;70m" : "[48;2;116;115;113m";
|
||||
var text = font.line(terminal, txt, Size.SMALL, Align.CENTER, new Custom.Width(btnWidth - 4),
|
||||
new Background(color));
|
||||
buttonHeight = text.getHeight() + 4;
|
||||
var lines = text.getData().split("\n");
|
||||
|
||||
for (int y = 0; y < buttonHeight; y++) {
|
||||
buffer.append(" ".repeat(leftPad));
|
||||
if (y < 2 || y >= buttonHeight - 2) {
|
||||
buffer.append("\033").append(color).append(" ".repeat(btnWidth));
|
||||
} else {
|
||||
buffer.append("\033").append(color).append(" ".repeat(2));
|
||||
buffer.append(lines[y - 2]);
|
||||
buffer.append("\033").append(color).append(" ".repeat(2));
|
||||
}
|
||||
buffer.append("\033[0m\n");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleMouse(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game) {
|
||||
int x = mouseEvent.getX();
|
||||
int y = mouseEvent.getY();
|
||||
var type = mouseEvent.getType();
|
||||
|
||||
int buttonx = x - leftPad;
|
||||
int buttony = y - (mainTextHeight + textButtonMargin) - 2;
|
||||
int margin = textButtonMargin / 2;
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
if (buttonx > 0 && buttonx <= btnWidth) {
|
||||
int top = (buttonHeight + margin);
|
||||
int bottom = top + buttonHeight;
|
||||
|
||||
if (buttony > top && buttony < bottom) {
|
||||
if (type == MouseEvent.Type.Pressed) {
|
||||
game.setWindow(Window.WORLD);
|
||||
return;
|
||||
}
|
||||
if (!buttonHover) {
|
||||
buttonHover = true;
|
||||
changed = true;
|
||||
}
|
||||
} else if (buttonHover) {
|
||||
buttonHover = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
screenRenderer.render(game);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
package cz.jzitnik.game.ui;
|
||||
|
||||
public enum Window {
|
||||
WORLD, INVENTORY, CRAFTING_TABLE, CHEST, FURNACE, ESC, SAVE_EXIT, SAVED, OPTIONS
|
||||
WORLD, INVENTORY, CRAFTING_TABLE, CHEST, FURNACE, ESC, SAVE_EXIT, SAVED, OPTIONS, DEATH_SCREEN,
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ public class ScreenRenderer {
|
||||
case OPTIONS -> game.getGameStates().dependencies.options.render(main, terminal);
|
||||
case SAVE_EXIT -> game.getGameStates().dependencies.escape.renderSave(main, terminal);
|
||||
case SAVED -> game.getGameStates().dependencies.escape.renderSaved(main, terminal);
|
||||
case DEATH_SCREEN -> game.getGameStates().dependencies.deathScreen.render(main, terminal, game);
|
||||
case WORLD -> {
|
||||
// World
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class Menu {
|
||||
}
|
||||
}
|
||||
|
||||
private 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)));
|
||||
leftPad = (width - btnWidth) / 2;
|
||||
log.debug("Button width: {}px ", btnWidth);
|
||||
|
Reference in New Issue
Block a user