From e3f5c12b434f1d77326d9e1f276656c1a7937e93 Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Mon, 2 Feb 2026 21:06:46 +0100 Subject: [PATCH] feat: Player movement multiplayer --- .../connection/ConnectToAGameResponse.java | 10 +- .../messages/player/PlayerArrivalChange.java | 6 ++ .../socket/messages/player/PlayerJoined.java | 7 ++ .../socket/messages/player/PlayerMove.java | 2 +- .../messages/player/PlayerMovedInUrRoom.java | 1 + .../messages/player/PlayerRotation.java | 5 + .../events/handlers/FullRoomDrawHandler.java | 2 +- .../handlers/MouseMoveEventHandler.java | 4 +- .../handlers/PlayerMoveEventHandler.java | 26 +++-- .../events/handlers/RerenderPartHandler.java | 4 +- .../cz/jzitnik/client/game/GamePlayer.java | 10 ++ .../cz/jzitnik/client/game/GameState.java | 9 +- .../cz/jzitnik/client/game/OtherPlayer.java | 25 ++++- .../java/cz/jzitnik/client/game/Player.java | 6 +- .../mobs/tasks/MobFollowingPlayerTask.java | 2 +- .../cz/jzitnik/client/game/objects/Chest.java | 10 +- .../socket/events/ConnectGameHandler.java | 9 +- .../events/PlayerArrivalChangeHandler.java | 24 +++++ .../socket/events/PlayerJoinedHandler.java | 22 +++++ .../events/PlayerMovedInUrRoomHandler.java | 94 +++++++++++++++++++ .../jzitnik/client/utils/RerenderUtils.java | 43 +++++---- game/src/main/resources/setup/rooms.yaml | 56 +++++------ .../handlers/ConnectToAGameHandler.java | 21 ++++- .../events/handlers/CreateGameHandler.java | 2 +- .../events/handlers/PlayerMoveHandler.java | 2 +- .../java/cz/jzitnik/server/game/Player.java | 12 ++- 26 files changed, 322 insertions(+), 92 deletions(-) create mode 100644 common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerArrivalChange.java create mode 100644 common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerJoined.java create mode 100644 common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerRotation.java create mode 100644 game/src/main/java/cz/jzitnik/client/game/GamePlayer.java create mode 100644 game/src/main/java/cz/jzitnik/client/socket/events/PlayerArrivalChangeHandler.java create mode 100644 game/src/main/java/cz/jzitnik/client/socket/events/PlayerJoinedHandler.java create mode 100644 game/src/main/java/cz/jzitnik/client/socket/events/PlayerMovedInUrRoomHandler.java diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/game/connection/ConnectToAGameResponse.java b/common/src/main/java/cz/jzitnik/common/socket/messages/game/connection/ConnectToAGameResponse.java index d652d2a..da7d74a 100644 --- a/common/src/main/java/cz/jzitnik/common/socket/messages/game/connection/ConnectToAGameResponse.java +++ b/common/src/main/java/cz/jzitnik/common/socket/messages/game/connection/ConnectToAGameResponse.java @@ -3,18 +3,20 @@ package cz.jzitnik.common.socket.messages.game.connection; import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.socket.SocketMessage; -public record ConnectToAGameResponse(ResponseType responseType, PlayerCreation playerCreation) implements SocketMessage { +import java.util.List; + +public record ConnectToAGameResponse(ResponseType responseType, PlayerCreation playerCreation, List existingPlayers) implements SocketMessage { private enum ResponseType { GAME_DOES_NOT_EXIST, SUCCESS } public ConnectToAGameResponse() { - this(ResponseType.GAME_DOES_NOT_EXIST, null); + this(ResponseType.GAME_DOES_NOT_EXIST, null, null); } - public ConnectToAGameResponse(PlayerCreation playerCreation) { - this(ResponseType.SUCCESS, playerCreation); + public ConnectToAGameResponse(PlayerCreation playerCreation, List existingPlayers) { + this(ResponseType.SUCCESS, playerCreation, existingPlayers); } public boolean success() { diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerArrivalChange.java b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerArrivalChange.java new file mode 100644 index 0000000..4891171 --- /dev/null +++ b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerArrivalChange.java @@ -0,0 +1,6 @@ +package cz.jzitnik.common.socket.messages.player; + +import cz.jzitnik.common.socket.SocketMessage; + +public record PlayerArrivalChange(int id, boolean arrived) implements SocketMessage { +} diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerJoined.java b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerJoined.java new file mode 100644 index 0000000..c60aa5b --- /dev/null +++ b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerJoined.java @@ -0,0 +1,7 @@ +package cz.jzitnik.common.socket.messages.player; + +import cz.jzitnik.common.models.player.PlayerCreation; +import cz.jzitnik.common.socket.SocketMessage; + +public record PlayerJoined(PlayerCreation playerCreation) implements SocketMessage { +} 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 index 8df9a64..fdd52d9 100644 --- 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 @@ -3,5 +3,5 @@ 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 { +public record PlayerMove(RoomCords newCords, PlayerRotation playerRotation) 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 index 3559743..0870792 100644 --- 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 @@ -10,4 +10,5 @@ import lombok.Getter; public class PlayerMovedInUrRoom implements SocketMessage { private int playerId; private RoomCords cords; + private PlayerRotation playerRotation; } diff --git a/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerRotation.java b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerRotation.java new file mode 100644 index 0000000..968721b --- /dev/null +++ b/common/src/main/java/cz/jzitnik/common/socket/messages/player/PlayerRotation.java @@ -0,0 +1,5 @@ +package cz.jzitnik.common.socket.messages.player; + +public enum PlayerRotation { + FRONT, BACK, LEFT, RIGHT +} diff --git a/game/src/main/java/cz/jzitnik/client/events/handlers/FullRoomDrawHandler.java b/game/src/main/java/cz/jzitnik/client/events/handlers/FullRoomDrawHandler.java index e4c22cc..ca14c0e 100644 --- a/game/src/main/java/cz/jzitnik/client/events/handlers/FullRoomDrawHandler.java +++ b/game/src/main/java/cz/jzitnik/client/events/handlers/FullRoomDrawHandler.java @@ -75,7 +75,7 @@ public class FullRoomDrawHandler extends AbstractEventHandler { int startX = start.getX(); int startY = start.getY(); - RerenderUtils.rerenderPart(0, width - 1, 0, height - 1, startX, startY, currentRoom, room, player, playerTexture, screenBuffer, resourceManager, debugging); + RerenderUtils.rerenderPart(0, width - 1, 0, height - 1, startX, startY, currentRoom, room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers()); if (event.isFullRerender()) { globalIOHandlerRepository.renderAll(); } diff --git a/game/src/main/java/cz/jzitnik/client/events/handlers/MouseMoveEventHandler.java b/game/src/main/java/cz/jzitnik/client/events/handlers/MouseMoveEventHandler.java index 06c5c3c..dd4b13f 100644 --- a/game/src/main/java/cz/jzitnik/client/events/handlers/MouseMoveEventHandler.java +++ b/game/src/main/java/cz/jzitnik/client/events/handlers/MouseMoveEventHandler.java @@ -177,10 +177,10 @@ public class MouseMoveEventHandler extends AbstractEventHandler currentRoom, room, player, - playerTexture, screenBuffer, resourceManager, - debugging + debugging, + gameState.getOtherPlayers() ); parts.add(new RerenderScreen.ScreenPart( 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 ca0e2c5..7127b86 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 @@ -9,10 +9,7 @@ import cz.jzitnik.client.annotations.injectors.InjectState; import cz.jzitnik.client.config.Debugging; import cz.jzitnik.client.config.PlayerConfig; import cz.jzitnik.client.events.*; -import cz.jzitnik.client.game.GameRoom; -import cz.jzitnik.client.game.GameState; -import cz.jzitnik.client.game.Player; -import cz.jzitnik.client.game.ResourceManager; +import cz.jzitnik.client.game.*; import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.client.states.RenderState; import cz.jzitnik.client.states.ScreenBuffer; @@ -24,6 +21,7 @@ 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 cz.jzitnik.common.socket.messages.player.PlayerRotation; import lombok.extern.slf4j.Slf4j; import java.awt.image.BufferedImage; @@ -77,46 +75,46 @@ public class PlayerMoveEventHandler extends AbstractEventHandler { if (originalPlayerY <= 10) { if (originalPlayerX >= 80 && originalPlayerX <= 105) { - player.setPlayerRotation(Player.PlayerRotation.BACK); + player.setPlayerRotation(PlayerRotation.BACK); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.TOP)); } return; } playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() - moveStep, player.getCollider()); - player.setPlayerRotation(Player.PlayerRotation.BACK); + player.setPlayerRotation(PlayerRotation.BACK); } case 'a' -> { if (originalPlayerX <= 30) { if (originalPlayerY >= 35 && originalPlayerY <= 65) { - player.setPlayerRotation(Player.PlayerRotation.LEFT); + player.setPlayerRotation(PlayerRotation.LEFT); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.LEFT)); } return; } playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() - moveStep, playerCords.getY(), player.getCollider()); - player.setPlayerRotation(Player.PlayerRotation.LEFT); + player.setPlayerRotation(PlayerRotation.LEFT); } case 's' -> { if (originalPlayerY >= 110) { if (originalPlayerX >= 75 && originalPlayerX <= 105) { - player.setPlayerRotation(Player.PlayerRotation.FRONT); + player.setPlayerRotation(PlayerRotation.FRONT); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.BOTTOM)); } return; } playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() + moveStep, player.getCollider()); - player.setPlayerRotation(Player.PlayerRotation.FRONT); + player.setPlayerRotation(PlayerRotation.FRONT); } case 'd' -> { if (originalPlayerX >= 155) { if (originalPlayerY >= 40 && originalPlayerY <= 60) { - player.setPlayerRotation(Player.PlayerRotation.RIGHT); + player.setPlayerRotation(PlayerRotation.RIGHT); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.RIGHT)); } return; } playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() + moveStep, playerCords.getY(), player.getCollider()); - player.setPlayerRotation(Player.PlayerRotation.RIGHT); + player.setPlayerRotation(PlayerRotation.RIGHT); } } playerMovementState.setLastMovement(System.currentTimeMillis()); @@ -141,10 +139,10 @@ public class PlayerMoveEventHandler extends AbstractEventHandler { currentRoom, room, gameState.getPlayer(), - gameState.getPlayer().getTexture(resourceManager), screenBuffer, resourceManager, - debugging + debugging, + gameState.getOtherPlayers() ); eventManager.emitEvent( diff --git a/game/src/main/java/cz/jzitnik/client/game/GamePlayer.java b/game/src/main/java/cz/jzitnik/client/game/GamePlayer.java new file mode 100644 index 0000000..4e27c0a --- /dev/null +++ b/game/src/main/java/cz/jzitnik/client/game/GamePlayer.java @@ -0,0 +1,10 @@ +package cz.jzitnik.client.game; + +import cz.jzitnik.common.models.coordinates.RoomCords; + +import java.awt.image.BufferedImage; + +public interface GamePlayer { + RoomCords getPlayerCords(); + BufferedImage getTexture(ResourceManager resourceManager); +} diff --git a/game/src/main/java/cz/jzitnik/client/game/GameState.java b/game/src/main/java/cz/jzitnik/client/game/GameState.java index 13ecee1..1cfcf03 100644 --- a/game/src/main/java/cz/jzitnik/client/game/GameState.java +++ b/game/src/main/java/cz/jzitnik/client/game/GameState.java @@ -24,9 +24,16 @@ public class GameState { @Setter private Player player; - @Getter private final List otherPlayers = new ArrayList<>(); + public List getOtherPlayers() { + return otherPlayers.stream().filter(OtherPlayer::isVisible).toList(); + } + + public List getAllOtherPlayers() { + return otherPlayers; + } + @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 index e131693..d90b855 100644 --- a/game/src/main/java/cz/jzitnik/client/game/OtherPlayer.java +++ b/game/src/main/java/cz/jzitnik/client/game/OtherPlayer.java @@ -1,20 +1,41 @@ package cz.jzitnik.client.game; +import cz.jzitnik.client.game.mobs.HittableMob; import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.player.PlayerCreation; +import cz.jzitnik.common.socket.messages.player.PlayerRotation; import lombok.Getter; import lombok.Setter; +import java.awt.image.BufferedImage; + @Getter -public class OtherPlayer { +public class OtherPlayer implements GamePlayer { private final int id; private boolean hitAnimationOn = false; private final RoomCords playerCords; @Setter - private Player.PlayerRotation playerRotation = Player.PlayerRotation.FRONT; + private PlayerRotation playerRotation = PlayerRotation.FRONT; + @Setter + private boolean visible; public OtherPlayer(PlayerCreation playerCreation) { this.id = playerCreation.getId(); this.playerCords = playerCreation.getPlayerCords(); } + + public BufferedImage getTexture(ResourceManager resourceManager) { + BufferedImage resource = resourceManager.getResource(switch (playerRotation) { + case FRONT -> ResourceManager.Resource.PLAYER_FRONT; + case BACK -> ResourceManager.Resource.PLAYER_BACK; + case LEFT -> ResourceManager.Resource.PLAYER_LEFT; + case RIGHT -> ResourceManager.Resource.PLAYER_RIGHT; + }); + + if (hitAnimationOn) { + return HittableMob.applyRedFactor(resource); + } + + return resource; + } } 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 b2c15b8..5f01c25 100644 --- a/game/src/main/java/cz/jzitnik/client/game/Player.java +++ b/game/src/main/java/cz/jzitnik/client/game/Player.java @@ -11,6 +11,7 @@ import cz.jzitnik.client.utils.DependencyManager; import cz.jzitnik.client.utils.events.Event; import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.common.models.player.PlayerCreation; +import cz.jzitnik.common.socket.messages.player.PlayerRotation; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -23,7 +24,7 @@ import java.util.concurrent.TimeUnit; @Getter @Slf4j -public class Player { +public class Player implements GamePlayer { private final int id; public static final int MAX_STAMINA = 20; public static final int MAX_HEALTH = 30; @@ -155,7 +156,4 @@ public class Player { }, delayMs, TimeUnit.MILLISECONDS); } - public enum PlayerRotation { - FRONT, BACK, LEFT, RIGHT - } } diff --git a/game/src/main/java/cz/jzitnik/client/game/mobs/tasks/MobFollowingPlayerTask.java b/game/src/main/java/cz/jzitnik/client/game/mobs/tasks/MobFollowingPlayerTask.java index 1bced57..0a0b43d 100644 --- a/game/src/main/java/cz/jzitnik/client/game/mobs/tasks/MobFollowingPlayerTask.java +++ b/game/src/main/java/cz/jzitnik/client/game/mobs/tasks/MobFollowingPlayerTask.java @@ -96,7 +96,7 @@ public class MobFollowingPlayerTask extends MobRoomTask { Player player = gameState.getPlayer(); BufferedImage playerTexture = player.getTexture(resourceManager); - RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, gameState.getCurrentRoom(), room, player, playerTexture, screenBuffer, resourceManager, debugging); + RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, gameState.getCurrentRoom(), room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers()); eventManager.emitEvent(new Event[]{ new MouseMoveEvent(null), diff --git a/game/src/main/java/cz/jzitnik/client/game/objects/Chest.java b/game/src/main/java/cz/jzitnik/client/game/objects/Chest.java index f3d31de..3a08570 100644 --- a/game/src/main/java/cz/jzitnik/client/game/objects/Chest.java +++ b/game/src/main/java/cz/jzitnik/client/game/objects/Chest.java @@ -101,7 +101,6 @@ public final class Chest extends GameObject implements UIClickHandler { Player player = gameState.getPlayer(); BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture()); - BufferedImage playerTexture = player.getTexture(resourceManager); BufferedImage chestTexture = getTexture(); var buffer = screenBuffer.getRenderedBuffer(); @@ -141,7 +140,7 @@ public final class Chest extends GameObject implements UIClickHandler { renderMaxX = Math.max(renderMaxX, prevDisplayStartX + prevWidth); clearPreviousUI( - currentRoom, roomTexture, player, playerTexture, + currentRoom, roomTexture, player, resourceManager, buffer, overrideBuffer, start, guiStartY, prevGuiStartX, @@ -171,7 +170,7 @@ public final class Chest extends GameObject implements UIClickHandler { GameRoom room, BufferedImage roomTexture, Player player, - BufferedImage playerTexture, + ResourceManager resourceManager, Pixel[][] buffer, Pixel[][] overrideBuffer, RoomCords start, @@ -188,10 +187,11 @@ public final class Chest extends GameObject implements UIClickHandler { null, new HashSet<>(), player, - playerTexture, + resourceManager, x, y, - debugging + debugging, + gameState.getOtherPlayers() ).pixel(); buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel); diff --git a/game/src/main/java/cz/jzitnik/client/socket/events/ConnectGameHandler.java b/game/src/main/java/cz/jzitnik/client/socket/events/ConnectGameHandler.java index 597d761..37a1343 100644 --- a/game/src/main/java/cz/jzitnik/client/socket/events/ConnectGameHandler.java +++ b/game/src/main/java/cz/jzitnik/client/socket/events/ConnectGameHandler.java @@ -3,10 +3,12 @@ package cz.jzitnik.client.socket.events; import cz.jzitnik.client.annotations.SocketEventHandler; import cz.jzitnik.client.annotations.injectors.InjectDependency; import cz.jzitnik.client.annotations.injectors.InjectState; -import cz.jzitnik.client.events.FullRoomDraw; +import cz.jzitnik.client.events.TerminalResizeEvent; import cz.jzitnik.client.game.GameState; +import cz.jzitnik.client.game.OtherPlayer; import cz.jzitnik.client.game.Player; import cz.jzitnik.client.socket.AbstractSocketEventHandler; +import cz.jzitnik.client.states.TerminalState; import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse; import lombok.extern.slf4j.Slf4j; @@ -18,6 +20,8 @@ public class ConnectGameHandler extends AbstractSocketEventHandler { + @InjectState + private GameState gameState; + + @Override + public void handle(PlayerArrivalChange event) { + var allPlayers = gameState.getAllOtherPlayers(); + for (OtherPlayer player : allPlayers) { + if (player.getId() == event.id()) { + player.setVisible(event.arrived()); + } + } + } +} diff --git a/game/src/main/java/cz/jzitnik/client/socket/events/PlayerJoinedHandler.java b/game/src/main/java/cz/jzitnik/client/socket/events/PlayerJoinedHandler.java new file mode 100644 index 0000000..f01d126 --- /dev/null +++ b/game/src/main/java/cz/jzitnik/client/socket/events/PlayerJoinedHandler.java @@ -0,0 +1,22 @@ +package cz.jzitnik.client.socket.events; + +import cz.jzitnik.client.annotations.SocketEventHandler; +import cz.jzitnik.client.annotations.injectors.InjectState; +import cz.jzitnik.client.game.GameState; +import cz.jzitnik.client.game.OtherPlayer; +import cz.jzitnik.client.socket.AbstractSocketEventHandler; +import cz.jzitnik.common.socket.messages.player.PlayerJoined; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@SocketEventHandler(PlayerJoined.class) +public class PlayerJoinedHandler extends AbstractSocketEventHandler { + @InjectState + private GameState gameState; + + @Override + public void handle(PlayerJoined event) { + log.debug("Player joined: {}", event.playerCreation().getId()); + gameState.getAllOtherPlayers().add(new OtherPlayer(event.playerCreation())); + } +} diff --git a/game/src/main/java/cz/jzitnik/client/socket/events/PlayerMovedInUrRoomHandler.java b/game/src/main/java/cz/jzitnik/client/socket/events/PlayerMovedInUrRoomHandler.java new file mode 100644 index 0000000..84a15a6 --- /dev/null +++ b/game/src/main/java/cz/jzitnik/client/socket/events/PlayerMovedInUrRoomHandler.java @@ -0,0 +1,94 @@ +package cz.jzitnik.client.socket.events; + +import com.googlecode.lanterna.TerminalPosition; +import cz.jzitnik.client.annotations.SocketEventHandler; +import cz.jzitnik.client.annotations.injectors.InjectConfig; +import cz.jzitnik.client.annotations.injectors.InjectDependency; +import cz.jzitnik.client.annotations.injectors.InjectState; +import cz.jzitnik.client.config.Debugging; +import cz.jzitnik.client.events.MouseMoveEvent; +import cz.jzitnik.client.events.RerenderScreen; +import cz.jzitnik.client.game.GameRoom; +import cz.jzitnik.client.game.GameState; +import cz.jzitnik.client.game.OtherPlayer; +import cz.jzitnik.client.game.ResourceManager; +import cz.jzitnik.client.socket.AbstractSocketEventHandler; +import cz.jzitnik.client.states.ScreenBuffer; +import cz.jzitnik.client.states.TerminalState; +import cz.jzitnik.client.ui.Stats; +import cz.jzitnik.client.utils.RerenderUtils; +import cz.jzitnik.client.utils.events.Event; +import cz.jzitnik.client.utils.events.EventManager; +import cz.jzitnik.common.socket.messages.player.PlayerMovedInUrRoom; +import lombok.extern.slf4j.Slf4j; + +import java.awt.image.BufferedImage; + +@Slf4j +@SocketEventHandler(PlayerMovedInUrRoom.class) +public class PlayerMovedInUrRoomHandler extends AbstractSocketEventHandler { + @InjectState + private GameState gameState; + + @InjectDependency + private EventManager eventManager; + + @InjectDependency + private ResourceManager resourceManager; + + @InjectState + private ScreenBuffer screenBuffer; + + @InjectConfig + private Debugging debugging; + + @InjectState + private TerminalState terminalState; + + @Override + public void handle(PlayerMovedInUrRoom event) { + log.debug("Player moved: {}", event.getPlayerId()); + OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.getPlayerId()).findFirst().get(); + otherPlayer.setPlayerRotation(event.getPlayerRotation()); + + var oldCords = otherPlayer.getPlayerCords(); + var newCords = event.getCords(); + + GameRoom currentRoom = gameState.getCurrentRoom(); + BufferedImage playerTexture = otherPlayer.getTexture(resourceManager); + BufferedImage room = resourceManager.getResource(currentRoom.getTexture()); + int originalPlayerX = oldCords.getX(); + int originalPlayerY = oldCords.getY(); + int newPlayerX = newCords.getX(); + int newPlayerY = newCords.getY(); + + int forStartX = Math.min(originalPlayerX, newPlayerX); + int forStartY = Math.min(originalPlayerY, newPlayerY); + int forEndX = Math.max(originalPlayerX, newPlayerX) + playerTexture.getWidth(); + int forEndY = Math.max(originalPlayerY, newPlayerY) + playerTexture.getHeight(); + + var start = RerenderUtils.getStart(room, terminalState.getTerminalScreen().getTerminalSize()); + int startX = start.getX(); + int startY = start.getY(); + + otherPlayer.getPlayerCords().updateCords(newCords); + + RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, gameState.getPlayer(), screenBuffer, resourceManager, debugging, gameState.getOtherPlayers()); + + eventManager.emitEvent(new Event[]{ + new MouseMoveEvent(null), + new RerenderScreen( + new RerenderScreen.ScreenPart[]{ + new RerenderScreen.ScreenPart( + new TerminalPosition(forStartX + startX, forStartY + startY), + new TerminalPosition(forEndX + 1 + startX, forEndY + startY) + ), + new RerenderScreen.ScreenPart( + new TerminalPosition(Stats.OFFSET_X, Stats.OFFSET_X), + new TerminalPosition(Stats.OFFSET_X + Stats.WIDTH, Stats.OFFSET_Y + Stats.HEIGHT) + ) + } + ) + }); + } +} diff --git a/game/src/main/java/cz/jzitnik/client/utils/RerenderUtils.java b/game/src/main/java/cz/jzitnik/client/utils/RerenderUtils.java index c00aeff..752d533 100644 --- a/game/src/main/java/cz/jzitnik/client/utils/RerenderUtils.java +++ b/game/src/main/java/cz/jzitnik/client/utils/RerenderUtils.java @@ -4,9 +4,7 @@ import com.googlecode.lanterna.TerminalSize; import com.googlecode.lanterna.TextColor; import cz.jzitnik.client.config.Debugging; import cz.jzitnik.client.events.handlers.FullRoomDrawHandler; -import cz.jzitnik.client.game.GameRoom; -import cz.jzitnik.client.game.Player; -import cz.jzitnik.client.game.ResourceManager; +import cz.jzitnik.client.game.*; import cz.jzitnik.client.game.mobs.Mob; import cz.jzitnik.client.game.objects.DroppedItem; import cz.jzitnik.client.game.objects.GameObject; @@ -19,8 +17,11 @@ import lombok.extern.slf4j.Slf4j; import java.awt.image.BufferedImage; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j public class RerenderUtils { @@ -36,14 +37,14 @@ public class RerenderUtils { return new RoomCords(x, y); } - public static void rerenderPart(int startX, int endX, int startY, int endY, int screenStartX, int screenStartY, GameRoom currentRoom, BufferedImage room, Player player, BufferedImage playerTexture, ScreenBuffer screenBuffer, ResourceManager resourceManager, Debugging debugging) { + public static void rerenderPart(int startX, int endX, int startY, int endY, int screenStartX, int screenStartY, GameRoom currentRoom, BufferedImage room, Player player, ScreenBuffer screenBuffer, ResourceManager resourceManager, Debugging debugging, List otherPlayers) { var buffer = screenBuffer.getRenderedBuffer(); var overrideBuffer = currentRoom.getOverrideBuffer(); BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS); Set doorPositions = RerenderUtils.getDoorPositions(currentRoom); for (int x = startX; x <= endX; x++) { for (int y = startY; y <= endY; y++) { - PixelResult pixelResult = getPixel(currentRoom, room, doors, doorPositions, player, playerTexture, x, y, debugging); + PixelResult pixelResult = getPixel(currentRoom, room, doors, doorPositions, player, resourceManager, x, y, debugging, otherPlayers); int pixel = pixelResult.pixel(); int red = (pixel >> 16) & 0xff; @@ -63,26 +64,34 @@ public class RerenderUtils { public record PixelResult(int pixel, boolean isPlayer) { } - public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set doorPositions, Player player, BufferedImage playerTexture, int x, int y, Debugging debugging) { + public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set doorPositions, Player player, ResourceManager resourceManager, int x, int y, Debugging debugging, List visiblePlayer) { float factor = 1.5f; // brightness multiplier if (debugging.renderColliders() && currentRoom.getColliders().stream().anyMatch(collider -> collider.isWithin(new RoomCords(x, y)))) { return new PixelResult(0xFFFF0000, false); } - if (x >= player.getPlayerCords().getX() && x < player.getPlayerCords().getX() + playerTexture.getWidth() - 1 && y >= player.getPlayerCords().getY() && y <= player.getPlayerCords().getY() + playerTexture.getHeight() - 1) { - int relativeX = x - player.getPlayerCords().getX(); - int relativeY = y - player.getPlayerCords().getY(); - var playerCollider = player.getCollider(); + List players = Stream.concat( + Stream.of(player), + visiblePlayer.stream() + ).toList(); - if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) { - return new PixelResult(0xFFFF0000, false); - } + var playerCollider = player.getCollider(); + for (GamePlayer player1 : players) { + var playerTexture = player1.getTexture(resourceManager); + if (x >= player1.getPlayerCords().getX() && x < player1.getPlayerCords().getX() + playerTexture.getWidth() - 1 && y >= player1.getPlayerCords().getY() && y <= player1.getPlayerCords().getY() + playerTexture.getHeight() - 1) { + int relativeX = x - player1.getPlayerCords().getX(); + int relativeY = y - player1.getPlayerCords().getY(); - int pixel = playerTexture.getRGB(relativeX, relativeY); - int alpha = (pixel >> 24) & 0xff; + if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) { + return new PixelResult(0xFFFF0000, false); + } - if (alpha != 0) { - return new PixelResult(pixel, true); + int pixel = playerTexture.getRGB(relativeX, relativeY); + int alpha = (pixel >> 24) & 0xff; + + if (alpha != 0) { + return new PixelResult(pixel, true); + } } } diff --git a/game/src/main/resources/setup/rooms.yaml b/game/src/main/resources/setup/rooms.yaml index 8f3e353..723c351 100644 --- a/game/src/main/resources/setup/rooms.yaml +++ b/game/src/main/resources/setup/rooms.yaml @@ -1,33 +1,33 @@ - id: "spawn" texture: "ROOM1" - mobs: - - type: "hittable_drops" - texture: "PLAYER_FRONT" - cords: - x: 100 - y: 100 - collider: - start: - x: 0 - y: 52 - end: - x: 44 - y: 78 - health: 10 - itemsDrops: - - name: "Apple" - type: - name: "food" - addHealth: 1 - texture: "APPLE" - tasks: - - type: "blind_following_player" - speed: 1 - updateRateMs: 100 - - type: "attacking_player" - damage: 5 - reach: 15 - updateRateMs: 500 + #mobs: + # - type: "hittable_drops" + # texture: "PLAYER_FRONT" + # cords: + # x: 100 + # y: 100 + # collider: + # start: + # x: 0 + # y: 52 + # end: + # x: 44 + # y: 78 + # health: 10 + # itemsDrops: + # - name: "Apple" + # type: + # name: "food" + # addHealth: 1 + # texture: "APPLE" + # tasks: + # - type: "blind_following_player" + # speed: 1 + # updateRateMs: 100 + # - type: "attacking_player" + # damage: 5 + # reach: 15 + # updateRateMs: 500 west: "empty" east: "truhlaright" north: null diff --git a/server/src/main/java/cz/jzitnik/server/events/handlers/ConnectToAGameHandler.java b/server/src/main/java/cz/jzitnik/server/events/handlers/ConnectToAGameHandler.java index b0fc35e..ec47661 100644 --- a/server/src/main/java/cz/jzitnik/server/events/handlers/ConnectToAGameHandler.java +++ b/server/src/main/java/cz/jzitnik/server/events/handlers/ConnectToAGameHandler.java @@ -3,6 +3,8 @@ package cz.jzitnik.server.events.handlers; import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGame; import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse; +import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange; +import cz.jzitnik.common.socket.messages.player.PlayerJoined; import cz.jzitnik.server.annotations.EventHandler; import cz.jzitnik.server.context.GlobalContext; import cz.jzitnik.server.events.AbstractEventHandler; @@ -35,12 +37,23 @@ public class ConnectToAGameHandler extends AbstractEventHandler ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class); PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml")); - client.setPlayer(new Player(game.getPlayers().size(), player.getPlayerCords())); + player.setId(game.getPlayers().size()); + client.setPlayer(new Player(player)); client.setGame(game); - client.getPlayer().setCurrentRoom(globalContext.getProperties().getProperty("rooms.default")); + String defaultRoomId = globalContext.getProperties().getProperty("rooms.default"); + client.getPlayer().setCurrentRoom(defaultRoomId); + + client.getSession().sendMessage(new ConnectToAGameResponse(player, game.getPlayers().stream().map(client1 -> client1.getPlayer().toPlayerCreation()).toList())); + + for (Client cl : game.getPlayers()) { + cl.getSession().sendMessage(new PlayerJoined(player)); + + if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) { + cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), true)); + client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), true)); + } + } game.getPlayers().add(client); - - client.getSession().sendMessage(new ConnectToAGameResponse(player)); } } 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 597a937..798a2c8 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 @@ -35,7 +35,7 @@ public class CreateGameHandler extends AbstractEventHandler { PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml")); player.setId(id); - client.setPlayer(new Player(id, player.getPlayerCords())); + client.setPlayer(new Player(player)); CreateGameResponse gameResponse = new CreateGameResponse(pass, player); Game game = new Game( 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 index 816901c..4c8868b 100644 --- a/server/src/main/java/cz/jzitnik/server/events/handlers/PlayerMoveHandler.java +++ b/server/src/main/java/cz/jzitnik/server/events/handlers/PlayerMoveHandler.java @@ -19,7 +19,7 @@ public class PlayerMoveHandler extends AbstractEventHandler { 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())); + player.getSession().sendMessage(new PlayerMovedInUrRoom(client.getPlayer().getId(), event.newCords(), event.playerRotation())); } } } diff --git a/server/src/main/java/cz/jzitnik/server/game/Player.java b/server/src/main/java/cz/jzitnik/server/game/Player.java index 8fd519d..88b932e 100644 --- a/server/src/main/java/cz/jzitnik/server/game/Player.java +++ b/server/src/main/java/cz/jzitnik/server/game/Player.java @@ -1,15 +1,23 @@ package cz.jzitnik.server.game; import cz.jzitnik.common.models.coordinates.RoomCords; +import cz.jzitnik.common.models.player.PlayerCreation; 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; + + public Player(PlayerCreation creation) { + id = creation.getId(); + cords = creation.getPlayerCords(); + } + + public PlayerCreation toPlayerCreation() { + return new PlayerCreation(cords, null); + } }