diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b08d639..9abc593 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -14,7 +14,6 @@
-
@@ -24,7 +23,6 @@
-
@@ -34,7 +32,6 @@
-
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 1044c37..86993b2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,5 +8,5 @@
-
+
\ No newline at end of file
diff --git a/common/src/main/java/cz/jzitnik/common/Config.java b/common/src/main/java/cz/jzitnik/common/Config.java
new file mode 100644
index 0000000..de224e3
--- /dev/null
+++ b/common/src/main/java/cz/jzitnik/common/Config.java
@@ -0,0 +1,5 @@
+package cz.jzitnik.common;
+
+public class Config {
+ public static final int WORLD_PASSWORD_LENGTH = 5;
+}
diff --git a/common/src/main/java/cz/jzitnik/common/models/coordinates/RoomCords.java b/common/src/main/java/cz/jzitnik/common/models/coordinates/RoomCords.java
index 724f2ce..4534fb4 100644
--- a/common/src/main/java/cz/jzitnik/common/models/coordinates/RoomCords.java
+++ b/common/src/main/java/cz/jzitnik/common/models/coordinates/RoomCords.java
@@ -29,6 +29,10 @@ public class RoomCords implements Cloneable, Serializable {
this.y = y;
}
+ public void updateCords(RoomCords roomCords) {
+ updateCords(roomCords.getX(), roomCords.getY());
+ }
+
public void updateCordsWithColliders(List colliders, int x, int y, RoomPart playerCollider) {
var normalizedPlayerCollider = new RoomPart(
new RoomCords(playerCollider.getStart().getX() + x, playerCollider.getStart().getY() + y),
diff --git a/common/src/main/java/cz/jzitnik/common/models/player/PlayerCreation.java b/common/src/main/java/cz/jzitnik/common/models/player/PlayerCreation.java
index 04a6024..a8587ac 100644
--- a/common/src/main/java/cz/jzitnik/common/models/player/PlayerCreation.java
+++ b/common/src/main/java/cz/jzitnik/common/models/player/PlayerCreation.java
@@ -5,11 +5,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.coordinates.RoomPart;
import lombok.Getter;
+import lombok.Setter;
import java.io.Serializable;
@Getter
public final class PlayerCreation implements Serializable {
+ @Setter
+ private int id;
private final RoomCords playerCords;
private final RoomPart collider;
diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMove.java b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMove.java
new file mode 100644
index 0000000..8df9a64
--- /dev/null
+++ b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMove.java
@@ -0,0 +1,7 @@
+package cz.jzitnik.common.socket.messages.player;
+
+import cz.jzitnik.common.models.coordinates.RoomCords;
+import cz.jzitnik.common.socket.SocketMessage;
+
+public record PlayerMove(RoomCords newCords) implements SocketMessage {
+}
diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMovedInUrRoom.java b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMovedInUrRoom.java
new file mode 100644
index 0000000..3559743
--- /dev/null
+++ b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerMovedInUrRoom.java
@@ -0,0 +1,13 @@
+package cz.jzitnik.common.socket.messages.player;
+
+import cz.jzitnik.common.models.coordinates.RoomCords;
+import cz.jzitnik.common.socket.SocketMessage;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class PlayerMovedInUrRoom implements SocketMessage {
+ private int playerId;
+ private RoomCords cords;
+}
diff --git a/game/src/main/java/cz/jzitnik/client/events/handlers/PlayerMoveEventHandler.java b/game/src/main/java/cz/jzitnik/client/events/handlers/PlayerMoveEventHandler.java
index 9ab9718..ca0e2c5 100644
--- a/game/src/main/java/cz/jzitnik/client/events/handlers/PlayerMoveEventHandler.java
+++ b/game/src/main/java/cz/jzitnik/client/events/handlers/PlayerMoveEventHandler.java
@@ -8,10 +8,7 @@ import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.config.PlayerConfig;
-import cz.jzitnik.client.events.MouseMoveEvent;
-import cz.jzitnik.client.events.PlayerMoveEvent;
-import cz.jzitnik.client.events.RerenderScreen;
-import cz.jzitnik.client.events.RoomChangeEvent;
+import cz.jzitnik.client.events.*;
import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player;
@@ -26,6 +23,7 @@ import cz.jzitnik.client.utils.RerenderUtils;
import cz.jzitnik.client.utils.events.AbstractEventHandler;
import cz.jzitnik.client.utils.events.Event;
import cz.jzitnik.client.utils.events.EventManager;
+import cz.jzitnik.common.socket.messages.player.PlayerMove;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@@ -146,6 +144,7 @@ public class PlayerMoveEventHandler extends AbstractEventHandler otherPlayers = new ArrayList<>();
+
@Getter
@Setter
private Interactable interacting;
diff --git a/game/src/main/java/cz/jzitnik/client/game/OtherPlayer.java b/game/src/main/java/cz/jzitnik/client/game/OtherPlayer.java
new file mode 100644
index 0000000..e131693
--- /dev/null
+++ b/game/src/main/java/cz/jzitnik/client/game/OtherPlayer.java
@@ -0,0 +1,20 @@
+package cz.jzitnik.client.game;
+
+import cz.jzitnik.common.models.coordinates.RoomCords;
+import cz.jzitnik.common.models.player.PlayerCreation;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+public class OtherPlayer {
+ private final int id;
+ private boolean hitAnimationOn = false;
+ private final RoomCords playerCords;
+ @Setter
+ private Player.PlayerRotation playerRotation = Player.PlayerRotation.FRONT;
+
+ public OtherPlayer(PlayerCreation playerCreation) {
+ this.id = playerCreation.getId();
+ this.playerCords = playerCreation.getPlayerCords();
+ }
+}
diff --git a/game/src/main/java/cz/jzitnik/client/game/Player.java b/game/src/main/java/cz/jzitnik/client/game/Player.java
index 0a437fa..b2c15b8 100644
--- a/game/src/main/java/cz/jzitnik/client/game/Player.java
+++ b/game/src/main/java/cz/jzitnik/client/game/Player.java
@@ -1,7 +1,5 @@
package cz.jzitnik.client.game;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.client.events.RerenderPart;
import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.items.types.interfaces.WeaponInterface;
@@ -26,6 +24,7 @@ import java.util.concurrent.TimeUnit;
@Getter
@Slf4j
public class Player {
+ private final int id;
public static final int MAX_STAMINA = 20;
public static final int MAX_HEALTH = 30;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@@ -43,18 +42,10 @@ public class Player {
private boolean hitAnimationOn = false;
private ScheduledFuture> currentTimeoutHitAnimation = null;
- @JsonCreator
- public Player(
- @JsonProperty("playerCords") RoomCords playerCords,
- @JsonProperty("collider") RoomPart collider
- ) {
- this.playerCords = playerCords;
- this.collider = collider;
- }
-
public Player(PlayerCreation playerCreation) {
this.playerCords = playerCreation.getPlayerCords();
this.collider = playerCreation.getCollider();
+ this.id = playerCreation.getId();
}
public void increaseStamina() {
diff --git a/game/src/main/java/cz/jzitnik/client/game/setup/scenes/connect/ServerChoose.java b/game/src/main/java/cz/jzitnik/client/game/setup/scenes/connect/ServerChoose.java
index 2f483a8..888a378 100644
--- a/game/src/main/java/cz/jzitnik/client/game/setup/scenes/connect/ServerChoose.java
+++ b/game/src/main/java/cz/jzitnik/client/game/setup/scenes/connect/ServerChoose.java
@@ -2,6 +2,7 @@ package cz.jzitnik.client.game.setup.scenes.connect;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor;
+import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.input.KeyType;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.client.annotations.injectors.InjectDependency;
@@ -9,11 +10,13 @@ import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.KeyboardPressEvent;
import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.events.SendSocketMessageEvent;
+import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.screens.Screen;
import cz.jzitnik.client.screens.scenes.Scene;
import cz.jzitnik.client.socket.Client;
import cz.jzitnik.client.sound.SoundPlayer;
import cz.jzitnik.client.states.TerminalState;
+import cz.jzitnik.client.ui.Inventory;
import cz.jzitnik.client.ui.pixels.AlphaPixel;
import cz.jzitnik.client.ui.pixels.Empty;
import cz.jzitnik.client.ui.utils.Input;
@@ -22,10 +25,13 @@ import cz.jzitnik.client.utils.TextRenderer;
import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.game.creation.CreateGame;
import jakarta.websocket.DeploymentException;
+import cz.jzitnik.client.ui.utils.Button;
+import lombok.extern.slf4j.Slf4j;
import java.awt.*;
import java.io.IOException;
+@Slf4j
public class ServerChoose extends Scene {
public ServerChoose(DependencyManager dependencyManager) {
GameMenuAudioScreen gameMenuScreen = new GameMenuAudioScreen();
@@ -78,6 +84,12 @@ public class ServerChoose extends Scene {
@InjectDependency
private TextRenderer textRenderer;
+ @InjectState
+ private GameState gameState;
+
+ @InjectDependency
+ private DependencyManager dependencyManager;
+
private void renderInput(boolean refresh) {
var tg = terminalState.getTextGraphics();
TerminalScreen screen = terminalState.getTerminalScreen();
@@ -136,32 +148,12 @@ public class ServerChoose extends Scene {
AlphaPixel[][] selectServer = textRenderer.renderText("Enter server IP", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
- int padY = 10;
-
- for (int y = 0; y < selectServer.length; y += 2) {
- for (int x = 0; x < selectServer[y].length; x++) {
- AlphaPixel topPixel = selectServer[y][x];
-
- AlphaPixel bottomPixel;
- if (y + 1 < selectServer.length) {
- bottomPixel = selectServer[y + 1][x];
- } else {
- bottomPixel = new Empty();
- }
-
- int termX = x;
- int termY = padY / 2 + y / 2;
-
- tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
- tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
- tg.setCharacter(termX, termY, '▄');
- }
- }
+ render(selectServer, 0, 10, tg);
renderInput(false);
try {
- screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA);
+ screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -178,7 +170,10 @@ public class ServerChoose extends Scene {
connecting = true;
client.connect(ipBuffer.toString());
- eventManager.emitEvent(new SendSocketMessageEvent(new CreateGame()));
+ Screen screen = new ActionSelector();
+ dependencyManager.inject(screen);
+ gameState.setScreen(screen);
+ screen.fullRender();
} catch (DeploymentException | IOException e) {
connecting = false;
}
@@ -195,7 +190,7 @@ public class ServerChoose extends Scene {
return;
}
- if (event.getKeyStroke().getKeyType() == KeyType.Character) {
+ if (event.getKeyStroke().getKeyType() == KeyType.Character && !event.getKeyStroke().isCtrlDown()) {
ipBuffer.append(event.getKeyStroke().getCharacter());
renderInput(true);
}
@@ -205,4 +200,282 @@ public class ServerChoose extends Scene {
public void handleMouseAction(MouseAction event) {
}
}
+
+ private static final class ActionSelector extends Screen {
+ @InjectDependency
+ private TextRenderer textRenderer;
+
+ @InjectState
+ private TerminalState terminalState;
+
+ @InjectDependency
+ private EventManager eventManager;
+
+ @InjectState
+ private GameState gameState;
+
+ private int selectedIndex = -1;
+
+ @Override
+ public void fullRender() {
+ TerminalScreen screen = terminalState.getTerminalScreen();
+ var tg = terminalState.getTextGraphics();
+ screen.clear();
+ TerminalSize terminalSize = screen.getTerminalSize();
+
+ for (int y = 0; y < terminalSize.getRows(); y += 1) {
+ for (int x = 0; x < terminalSize.getColumns(); x++) {
+ tg.setBackgroundColor(TextColor.ANSI.BLACK);
+ tg.setForegroundColor(TextColor.ANSI.BLACK);
+ tg.setCharacter(x, y, '▄');
+ }
+ }
+
+ AlphaPixel[][] selectAction = textRenderer.renderText("Select action", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
+
+ render(selectAction, 0, 10, tg);
+ renderButtons();
+
+ try {
+ screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final int BUTTON_HEIGHT = 20;
+ private static final int BUTTON_WIDTH = 200;
+ private static final int BUTTON_GAP = 10;
+ private static final int BUTTON_COUNT = 2;
+ private static final int BUTTONS_HEIGHT = BUTTON_HEIGHT * BUTTON_COUNT + (BUTTON_COUNT - 1) * BUTTON_GAP;
+
+ private void renderButtons() {
+ var tg = terminalState.getTextGraphics();
+ TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize();
+
+ final int BUTTON_PAD_X = terminalSize.getColumns() / 2 - BUTTON_WIDTH / 2;
+ final int BUTTON_PAD_Y = terminalSize.getRows() - BUTTONS_HEIGHT / 2;
+
+ Button button = new Button(
+ Inventory.BORDER_COLOR,
+ Inventory.BACKGROUND_COLOR,
+ Inventory.BACKGROUND_COLOR_HOVERED,
+ Color.WHITE,
+ BUTTON_HEIGHT,
+ BUTTON_WIDTH,
+ 1,
+ 15f,
+ textRenderer
+ );
+
+ render(button.render("Create a world", selectedIndex == 0), BUTTON_PAD_X, BUTTON_PAD_Y, tg);
+ render(button.render("Connect to an existing world", selectedIndex == 1), BUTTON_PAD_X, BUTTON_PAD_Y + (BUTTON_HEIGHT + BUTTON_GAP), tg);
+ }
+
+ @Override
+ public void handleMouseAction(MouseAction event) {
+ TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize();
+
+ final int BUTTON_START_X = terminalSize.getColumns() / 2 - BUTTON_WIDTH / 2;
+ final int BUTTON_START_Y = terminalSize.getRows() - BUTTONS_HEIGHT / 2;
+ final int BUTTON_END_X = BUTTON_START_X + BUTTON_WIDTH;
+ final int BUTTON_END_Y = BUTTON_START_Y + BUTTONS_HEIGHT;
+
+ final int TERMINAL_X = event.getPosition().getColumn();
+ final int TERMINAL_Y = event.getPosition().getRow() * 2;
+
+ final int SINGLE_BUTTON_HEIGHT = BUTTON_HEIGHT + BUTTON_GAP;
+
+ int index = (TERMINAL_Y - BUTTON_START_Y) / SINGLE_BUTTON_HEIGHT;
+ int rest = (TERMINAL_Y - BUTTON_START_Y) % SINGLE_BUTTON_HEIGHT;
+
+ if (!(TERMINAL_X >= BUTTON_START_X && TERMINAL_Y >= BUTTON_START_Y && TERMINAL_X < BUTTON_END_X && TERMINAL_Y < BUTTON_END_Y) || rest > BUTTON_HEIGHT) {
+ if (selectedIndex != -1) {
+ selectedIndex = -1;
+ renderButtons();
+ refresh();
+ }
+ return;
+ }
+
+ switch (event.getActionType()) {
+ case MOVE -> {
+ selectedIndex = index;
+ renderButtons();
+ refresh();
+ }
+ case CLICK_RELEASE -> {
+ switch (index) {
+ case 0 -> eventManager.emitEvent(new SendSocketMessageEvent(new CreateGame()));
+ case 1 -> {
+ Screen screen = new ConnectWorld();
+ gameState.setScreen(screen);
+ screen.fullRender();
+ }
+ }
+ }
+ }
+ }
+
+ private void refresh() {
+ try {
+ terminalState.getTerminalScreen().refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void handleKeyboardAction(KeyboardPressEvent event) {
+
+ }
+ }
+
+
+ private static final class ConnectWorld extends Screen {
+ private final StringBuilder passBuffer = new StringBuilder();
+ private boolean connecting = false;
+
+ @InjectDependency
+ private EventManager eventManager;
+
+ @InjectDependency
+ private Client client;
+
+ @InjectState
+ private TerminalState terminalState;
+
+ @InjectDependency
+ private TextRenderer textRenderer;
+
+ @InjectState
+ private GameState gameState;
+
+ @InjectDependency
+ private DependencyManager dependencyManager;
+
+ private void renderInput(boolean refresh) {
+ var tg = terminalState.getTextGraphics();
+ TerminalScreen screen = terminalState.getTerminalScreen();
+ Input input = new Input(passBuffer.toString(), 18, 100);
+ var inputBuffer = input.render(textRenderer);
+ TerminalSize termSize = screen.getTerminalSize();
+ int renderPixelWidth = inputBuffer[0].length;
+ int renderPixelHeight = inputBuffer.length;
+ int renderCharWidth = renderPixelWidth;
+ int renderCharHeight = (renderPixelHeight + 1) / 2;
+ int startX = (termSize.getColumns() - renderCharWidth) / 2;
+ int startY = (termSize.getRows() - renderCharHeight) / 2;
+
+ for (int y = 0; y < inputBuffer.length; y += 2) {
+ for (int x = 0; x < inputBuffer[y].length; x++) {
+ AlphaPixel bottomPixel;
+ AlphaPixel topPixel = inputBuffer[y][x];
+ if (y + 1 < inputBuffer.length) {
+ bottomPixel = inputBuffer[y + 1][x];
+ } else {
+ bottomPixel = new Empty();
+ }
+
+ int termX = startX + x;
+ int termY = startY + (y / 2);
+
+ tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
+ tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
+ tg.setCharacter(termX, termY, '▄');
+ }
+ }
+
+ if (refresh) {
+ try {
+ screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void fullRender() {
+ TerminalScreen screen = terminalState.getTerminalScreen();
+ var tg = terminalState.getTextGraphics();
+ screen.clear();
+ TerminalSize terminalSize = screen.getTerminalSize();
+
+ for (int y = 0; y < terminalSize.getRows(); y += 1) {
+ for (int x = 0; x < terminalSize.getColumns(); x++) {
+ tg.setBackgroundColor(TextColor.ANSI.BLACK);
+ tg.setForegroundColor(TextColor.ANSI.BLACK);
+ tg.setCharacter(x, y, '▄');
+ }
+ }
+
+ AlphaPixel[][] selectServer = textRenderer.renderText("Enter world password", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
+
+ render(selectServer, 0, 10, tg);
+
+ renderInput(false);
+
+ try {
+ screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void handleKeyboardAction(KeyboardPressEvent event) {
+ if (connecting) {
+ return;
+ }
+
+ if (event.getKeyStroke().getKeyType() == KeyType.Enter) {
+ connecting = true;
+ String pass = passBuffer.toString();
+
+ return;
+ }
+
+ if (event.getKeyStroke().getKeyType() == KeyType.Backspace) {
+ if (passBuffer.isEmpty()) {
+ return;
+ }
+
+ passBuffer.deleteCharAt(passBuffer.length() - 1);
+ renderInput(true);
+ return;
+ }
+
+ if (event.getKeyStroke().getKeyType() == KeyType.Character && !event.getKeyStroke().isCtrlDown()) {
+ passBuffer.append(event.getKeyStroke().getCharacter());
+ renderInput(true);
+ }
+ }
+
+ @Override
+ public void handleMouseAction(MouseAction event) {
+ }
+ }
+
+ private static void render(AlphaPixel[][] buffer, int padX, int padY, TextGraphics tg) {
+ for (int y = 0; y < buffer.length; y += 2) {
+ for (int x = 0; x < buffer[y].length; x++) {
+ AlphaPixel topPixel = buffer[y][x];
+
+ AlphaPixel bottomPixel;
+ if (y + 1 < buffer.length) {
+ bottomPixel = buffer[y + 1][x];
+ } else {
+ bottomPixel = new Empty();
+ }
+
+ int termX = padX + x;
+ int termY = padY / 2 + y / 2;
+
+ tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
+ tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
+ tg.setCharacter(termX, termY, '▄');
+ }
+ }
+ }
}
diff --git a/game/src/main/java/cz/jzitnik/client/ui/Inventory.java b/game/src/main/java/cz/jzitnik/client/ui/Inventory.java
index ed2306c..8390187 100644
--- a/game/src/main/java/cz/jzitnik/client/ui/Inventory.java
+++ b/game/src/main/java/cz/jzitnik/client/ui/Inventory.java
@@ -18,6 +18,7 @@ import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.items.types.InteractableItem;
import cz.jzitnik.client.game.objects.DroppedItem;
+import cz.jzitnik.client.ui.pixels.AlphaPixel;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.states.ScreenBuffer;
import cz.jzitnik.client.states.TerminalState;
@@ -50,17 +51,17 @@ public class Inventory {
private static final int INNER_BORDER_WIDTH = 1;
private static final int ITEM_SIZE = 16; // Characters
private static final int ITEM_PADDING = 2; // padding on each side
- public static final Pixel BORDER_COLOR =
+ public static final AlphaPixel BORDER_COLOR =
new ColoredPixel(new TextColor.RGB(41, 29, 19));
- public static final Pixel BACKGROUND_COLOR =
+ public static final AlphaPixel BACKGROUND_COLOR =
new ColoredPixel(new TextColor.RGB(61, 45, 29));
- private static final Pixel BACKGROUND_COLOR_HOVERED =
+ public static final AlphaPixel BACKGROUND_COLOR_HOVERED =
new ColoredPixel(new TextColor.RGB(77, 56, 36));
- private static final Pixel BACKGROUND_COLOR_SELECTED =
+ private static final AlphaPixel BACKGROUND_COLOR_SELECTED =
new ColoredPixel(new TextColor.RGB(95, 62, 32));
- private static final Pixel BACKGROUND_COLOR_EQUIPPED =
+ private static final AlphaPixel BACKGROUND_COLOR_EQUIPPED =
new ColoredPixel(new TextColor.RGB(50, 145, 150));
- private static final Pixel BACKGROUND_COLOR_EQUIPPED_SELECTED =
+ private static final AlphaPixel BACKGROUND_COLOR_EQUIPPED_SELECTED =
new ColoredPixel(new TextColor.RGB(58, 170, 176));
private static final int ITEM_SLOT_SIZE = ITEM_SIZE + ITEM_PADDING * 2;
public static final int INVENTORY_WIDTH =
diff --git a/game/src/main/java/cz/jzitnik/client/ui/pixels/AlphaPixel.java b/game/src/main/java/cz/jzitnik/client/ui/pixels/AlphaPixel.java
index b6819fb..a7e13f8 100644
--- a/game/src/main/java/cz/jzitnik/client/ui/pixels/AlphaPixel.java
+++ b/game/src/main/java/cz/jzitnik/client/ui/pixels/AlphaPixel.java
@@ -10,4 +10,8 @@ public sealed abstract class AlphaPixel extends Pixel permits Empty, ColoredPixe
super(color);
this.alpha = alpha;
}
+
+ public boolean isTransparent() {
+ return alpha == 0f;
+ }
}
diff --git a/game/src/main/java/cz/jzitnik/client/ui/utils/Button.java b/game/src/main/java/cz/jzitnik/client/ui/utils/Button.java
new file mode 100644
index 0000000..c2876fb
--- /dev/null
+++ b/game/src/main/java/cz/jzitnik/client/ui/utils/Button.java
@@ -0,0 +1,50 @@
+package cz.jzitnik.client.ui.utils;
+
+import cz.jzitnik.client.ui.pixels.AlphaPixel;
+import cz.jzitnik.client.utils.TextRenderer;
+
+import java.awt.*;
+
+public record Button(
+ AlphaPixel borderColor,
+ AlphaPixel backgroundColor,
+ AlphaPixel backgroundSelectedColor,
+ Color textColor,
+ int height,
+ int width,
+ int borderWidth,
+ float fontSize,
+ TextRenderer textRenderer
+) {
+ public AlphaPixel[][] render(String text, boolean selected) {
+ AlphaPixel[][] buf = new AlphaPixel[height][width];
+ AlphaPixel[][] textBuf = textRenderer.renderTextSingleLine(text, width - borderWidth * 2, height - borderWidth * 2, textColor, fontSize, true, true);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (x < borderWidth || y < borderWidth || x >= width - borderWidth || y >= height - borderWidth) {
+ buf[y][x] = borderColor;
+ continue;
+ }
+
+ if (x == borderWidth || y == borderWidth || x - 1 == width - borderWidth || y == height - borderWidth) {
+ buf[y][x] = selected ? backgroundSelectedColor : backgroundColor;
+ continue;
+ }
+
+ int textActualX = x - borderWidth - 1;
+ int textActualY = y - borderWidth - 1;
+ AlphaPixel textPixel = textBuf[textActualY][textActualX];
+
+ if (!textPixel.isTransparent()) {
+ buf[y][x] = textPixel;
+ continue;
+ }
+
+ buf[y][x] = selected ? backgroundSelectedColor : backgroundColor;
+ }
+ }
+
+ return buf;
+ }
+}
diff --git a/server/src/main/java/cz/jzitnik/server/context/GlobalContext.java b/server/src/main/java/cz/jzitnik/server/context/GlobalContext.java
index 52519d2..d4cd1fb 100644
--- a/server/src/main/java/cz/jzitnik/server/context/GlobalContext.java
+++ b/server/src/main/java/cz/jzitnik/server/context/GlobalContext.java
@@ -2,23 +2,52 @@ package cz.jzitnik.server.context;
import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.events.EventManager;
-import cz.jzitnik.server.socket.SocketSession;
+import cz.jzitnik.server.game.Game;
import jakarta.websocket.Session;
import lombok.Getter;
import lombok.Setter;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
public class GlobalContext {
@Getter
private final HashMap sessions = new HashMap<>();
+ @Getter
+ private final Set games = new HashSet<>();
+
@Getter
@Setter
private EventManager eventManager;
- public void registerClient(Session session) {
- Client client = new Client(new SocketSession(session));
- sessions.put(session, client);
+ @Getter
+ private final Properties properties;
+
+ public void registerClient(Client client) {
+ sessions.put(client.getSession().getSession(), client);
+ }
+
+ public GlobalContext() {
+ Properties props = new Properties();
+
+ try (InputStream input = GlobalContext.class
+ .getClassLoader()
+ .getResourceAsStream("config.properties")) {
+
+ if (input == null) {
+ throw new RuntimeException("config.properties not found");
+ }
+
+ props.load(input);
+
+ this.properties = props;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/server/src/main/java/cz/jzitnik/server/events/handlers/CreateGameHandler.java b/server/src/main/java/cz/jzitnik/server/events/handlers/CreateGameHandler.java
index c23b3b7..410344f 100644
--- a/server/src/main/java/cz/jzitnik/server/events/handlers/CreateGameHandler.java
+++ b/server/src/main/java/cz/jzitnik/server/events/handlers/CreateGameHandler.java
@@ -1,5 +1,6 @@
package cz.jzitnik.server.events.handlers;
+import cz.jzitnik.common.Config;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.game.creation.CreateGame;
import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse;
@@ -7,11 +8,17 @@ import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
+import cz.jzitnik.server.game.Game;
+import cz.jzitnik.server.game.Player;
+import cz.jzitnik.server.utils.PasswordGenerator;
import lombok.RequiredArgsConstructor;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader;
import tools.jackson.dataformat.yaml.YAMLFactory;
+import java.util.ArrayList;
+import java.util.List;
+
@RequiredArgsConstructor
@EventHandler(CreateGame.class)
public class CreateGameHandler extends AbstractEventHandler {
@@ -20,13 +27,24 @@ public class CreateGameHandler extends AbstractEventHandler {
@Override
public void handle(CreateGame event, Client client) {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
- String pass = "nevim"; // TODO: Generate
+ String pass = PasswordGenerator.generatePassword(Config.WORLD_PASSWORD_LENGTH); // TODO: Generate
+ int id = 0; // Owners id is always 0
ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class);
PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml"));
+ player.setId(id);
+
+ client.setPlayer(new Player(id, player.getPlayerCords()));
CreateGameResponse gameResponse = new CreateGameResponse(pass, player);
+ Game game = new Game(
+ pass,
+ new ArrayList<>(List.of(client))
+ );
+ client.setGame(game);
+ client.getPlayer().setCurrentRoom(globalContext.getProperties().getProperty("rooms.default"));
+ globalContext.getGames().add(game);
- client.session().sendMessage(gameResponse);
+ client.getSession().sendMessage(gameResponse);
}
}
diff --git a/server/src/main/java/cz/jzitnik/server/events/handlers/PlayerMoveHandler.java b/server/src/main/java/cz/jzitnik/server/events/handlers/PlayerMoveHandler.java
new file mode 100644
index 0000000..67a74d1
--- /dev/null
+++ b/server/src/main/java/cz/jzitnik/server/events/handlers/PlayerMoveHandler.java
@@ -0,0 +1,26 @@
+package cz.jzitnik.server.events.handlers;
+
+import cz.jzitnik.common.socket.messages.player.PlayerMove;
+import cz.jzitnik.common.socket.messages.player.PlayerMovedInUrRoom;
+import cz.jzitnik.server.annotations.EventHandler;
+import cz.jzitnik.server.context.GlobalContext;
+import cz.jzitnik.server.events.AbstractEventHandler;
+import cz.jzitnik.server.game.Client;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@EventHandler(PlayerMove.class)
+public class PlayerMoveHandler extends AbstractEventHandler {
+ private final GlobalContext globalContext;
+
+ @Override
+ public void handle(PlayerMove event, Client client) {
+ client.getPlayer().getCords().updateCords(event.newCords());
+
+ for (Client player : client.getGame().getPlayers()) {
+ if (player.getPlayer().getCurrentRoom().equals(client.getPlayer().getCurrentRoom()) && player.getPlayer().getId() != client.getPlayer().getId()) {
+ player.getSession().sendMessage(new PlayerMovedInUrRoom(client.getPlayer().getId(), event.newCords()));
+ }
+ }
+ }
+}
diff --git a/server/src/main/java/cz/jzitnik/server/game/Client.java b/server/src/main/java/cz/jzitnik/server/game/Client.java
index 0ddae7f..32394d4 100644
--- a/server/src/main/java/cz/jzitnik/server/game/Client.java
+++ b/server/src/main/java/cz/jzitnik/server/game/Client.java
@@ -1,5 +1,25 @@
package cz.jzitnik.server.game;
import cz.jzitnik.server.socket.SocketSession;
+import lombok.Getter;
+import lombok.Setter;
-public record Client(SocketSession session) {}
\ No newline at end of file
+@Getter
+public final class Client {
+ private final SocketSession session;
+ @Setter
+ private Player player;
+ @Setter
+ private Game game;
+
+ public Client(SocketSession session, Player player, Game game) {
+ this.session = session;
+ this.player = player;
+ this.game = game;
+ }
+
+ public Client(SocketSession session, Player player) {
+ this(session, player, null);
+ }
+
+}
\ No newline at end of file
diff --git a/server/src/main/java/cz/jzitnik/server/game/Game.java b/server/src/main/java/cz/jzitnik/server/game/Game.java
index 42011b3..27a421f 100644
--- a/server/src/main/java/cz/jzitnik/server/game/Game.java
+++ b/server/src/main/java/cz/jzitnik/server/game/Game.java
@@ -1,4 +1,13 @@
package cz.jzitnik.server.game;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+@AllArgsConstructor
public class Game {
+ private String password;
+ private List players;
}
diff --git a/server/src/main/java/cz/jzitnik/server/game/Player.java b/server/src/main/java/cz/jzitnik/server/game/Player.java
new file mode 100644
index 0000000..8fd519d
--- /dev/null
+++ b/server/src/main/java/cz/jzitnik/server/game/Player.java
@@ -0,0 +1,15 @@
+package cz.jzitnik.server.game;
+
+import cz.jzitnik.common.models.coordinates.RoomCords;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@RequiredArgsConstructor
+public class Player {
+ private final int id;
+ private final RoomCords cords;
+ @Setter
+ private String currentRoom;
+}
diff --git a/server/src/main/java/cz/jzitnik/server/socket/WebSocket.java b/server/src/main/java/cz/jzitnik/server/socket/WebSocket.java
index 623bc86..e43368d 100644
--- a/server/src/main/java/cz/jzitnik/server/socket/WebSocket.java
+++ b/server/src/main/java/cz/jzitnik/server/socket/WebSocket.java
@@ -18,7 +18,8 @@ public class WebSocket {
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
this.globalContext = (GlobalContext) config.getUserProperties().get("globalContext");
- globalContext.registerClient(session);
+
+ globalContext.registerClient(new Client(new SocketSession(session), null));
log.debug("Client connected: {}", session.getId());
}
diff --git a/server/src/main/java/cz/jzitnik/server/utils/PasswordGenerator.java b/server/src/main/java/cz/jzitnik/server/utils/PasswordGenerator.java
new file mode 100644
index 0000000..2f4423f
--- /dev/null
+++ b/server/src/main/java/cz/jzitnik/server/utils/PasswordGenerator.java
@@ -0,0 +1,23 @@
+package cz.jzitnik.server.utils;
+
+import java.security.SecureRandom;
+
+public class PasswordGenerator {
+ private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ private static final SecureRandom RANDOM = new SecureRandom();
+
+ public static String generatePassword(int length) {
+ if (length <= 0) {
+ throw new IllegalArgumentException("Password length must be greater than 0");
+ }
+
+ StringBuilder password = new StringBuilder(length);
+
+ for (int i = 0; i < length; i++) {
+ int index = RANDOM.nextInt(CHARACTERS.length());
+ password.append(CHARACTERS.charAt(index));
+ }
+
+ return password.toString();
+ }
+}
diff --git a/server/src/main/resources/config.properties b/server/src/main/resources/config.properties
new file mode 100644
index 0000000..503483b
--- /dev/null
+++ b/server/src/main/resources/config.properties
@@ -0,0 +1 @@
+rooms.default=spawn
\ No newline at end of file