feat: Multiplayer #2

Merged
jzitnik merged 14 commits from multiplayer into main 2026-02-04 10:33:25 +00:00
26 changed files with 322 additions and 92 deletions
Showing only changes of commit e3f5c12b43 - Show all commits

View File

@@ -3,18 +3,20 @@ package cz.jzitnik.common.socket.messages.game.connection;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.SocketMessage; 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<PlayerCreation> existingPlayers) implements SocketMessage {
private enum ResponseType { private enum ResponseType {
GAME_DOES_NOT_EXIST, GAME_DOES_NOT_EXIST,
SUCCESS SUCCESS
} }
public ConnectToAGameResponse() { public ConnectToAGameResponse() {
this(ResponseType.GAME_DOES_NOT_EXIST, null); this(ResponseType.GAME_DOES_NOT_EXIST, null, null);
} }
public ConnectToAGameResponse(PlayerCreation playerCreation) { public ConnectToAGameResponse(PlayerCreation playerCreation, List<PlayerCreation> existingPlayers) {
this(ResponseType.SUCCESS, playerCreation); this(ResponseType.SUCCESS, playerCreation, existingPlayers);
} }
public boolean success() { public boolean success() {

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -3,5 +3,5 @@ package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage; import cz.jzitnik.common.socket.SocketMessage;
public record PlayerMove(RoomCords newCords) implements SocketMessage { public record PlayerMove(RoomCords newCords, PlayerRotation playerRotation) implements SocketMessage {
} }

View File

@@ -10,4 +10,5 @@ import lombok.Getter;
public class PlayerMovedInUrRoom implements SocketMessage { public class PlayerMovedInUrRoom implements SocketMessage {
private int playerId; private int playerId;
private RoomCords cords; private RoomCords cords;
private PlayerRotation playerRotation;
} }

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.common.socket.messages.player;
public enum PlayerRotation {
FRONT, BACK, LEFT, RIGHT
}

View File

@@ -75,7 +75,7 @@ public class FullRoomDrawHandler extends AbstractEventHandler<FullRoomDraw> {
int startX = start.getX(); int startX = start.getX();
int startY = start.getY(); 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()) { if (event.isFullRerender()) {
globalIOHandlerRepository.renderAll(); globalIOHandlerRepository.renderAll();
} }

View File

@@ -177,10 +177,10 @@ public class MouseMoveEventHandler extends AbstractEventHandler<MouseMoveEvent>
currentRoom, currentRoom,
room, room,
player, player,
playerTexture,
screenBuffer, screenBuffer,
resourceManager, resourceManager,
debugging debugging,
gameState.getOtherPlayers()
); );
parts.add(new RerenderScreen.ScreenPart( parts.add(new RerenderScreen.ScreenPart(

View File

@@ -9,10 +9,7 @@ import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.config.Debugging; import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.config.PlayerConfig; import cz.jzitnik.client.config.PlayerConfig;
import cz.jzitnik.client.events.*; import cz.jzitnik.client.events.*;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.*;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.states.RenderState; import cz.jzitnik.client.states.RenderState;
import cz.jzitnik.client.states.ScreenBuffer; 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.Event;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.player.PlayerMove; import cz.jzitnik.common.socket.messages.player.PlayerMove;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -77,46 +75,46 @@ public class PlayerMoveEventHandler extends AbstractEventHandler<PlayerMoveEvent
case 'w' -> { case 'w' -> {
if (originalPlayerY <= 10) { if (originalPlayerY <= 10) {
if (originalPlayerX >= 80 && originalPlayerX <= 105) { if (originalPlayerX >= 80 && originalPlayerX <= 105) {
player.setPlayerRotation(Player.PlayerRotation.BACK); player.setPlayerRotation(PlayerRotation.BACK);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.TOP)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.TOP));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() - moveStep, player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() - moveStep, player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.BACK); player.setPlayerRotation(PlayerRotation.BACK);
} }
case 'a' -> { case 'a' -> {
if (originalPlayerX <= 30) { if (originalPlayerX <= 30) {
if (originalPlayerY >= 35 && originalPlayerY <= 65) { if (originalPlayerY >= 35 && originalPlayerY <= 65) {
player.setPlayerRotation(Player.PlayerRotation.LEFT); player.setPlayerRotation(PlayerRotation.LEFT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.LEFT)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.LEFT));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() - moveStep, playerCords.getY(), player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() - moveStep, playerCords.getY(), player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.LEFT); player.setPlayerRotation(PlayerRotation.LEFT);
} }
case 's' -> { case 's' -> {
if (originalPlayerY >= 110) { if (originalPlayerY >= 110) {
if (originalPlayerX >= 75 && originalPlayerX <= 105) { if (originalPlayerX >= 75 && originalPlayerX <= 105) {
player.setPlayerRotation(Player.PlayerRotation.FRONT); player.setPlayerRotation(PlayerRotation.FRONT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.BOTTOM)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.BOTTOM));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() + moveStep, player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() + moveStep, player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.FRONT); player.setPlayerRotation(PlayerRotation.FRONT);
} }
case 'd' -> { case 'd' -> {
if (originalPlayerX >= 155) { if (originalPlayerX >= 155) {
if (originalPlayerY >= 40 && originalPlayerY <= 60) { if (originalPlayerY >= 40 && originalPlayerY <= 60) {
player.setPlayerRotation(Player.PlayerRotation.RIGHT); player.setPlayerRotation(PlayerRotation.RIGHT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.RIGHT)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.RIGHT));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() + moveStep, playerCords.getY(), player.getCollider()); 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()); playerMovementState.setLastMovement(System.currentTimeMillis());
@@ -141,10 +139,10 @@ public class PlayerMoveEventHandler extends AbstractEventHandler<PlayerMoveEvent
int startX = start.getX(); int startX = start.getX();
int startY = start.getY(); int startY = start.getY();
RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, player, playerTexture, screenBuffer, resourceManager, debugging); RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers());
eventManager.emitEvent(new Event[]{ eventManager.emitEvent(new Event[]{
new SendSocketMessageEvent(new PlayerMove(playerCords)), new SendSocketMessageEvent(new PlayerMove(playerCords, player.getPlayerRotation())),
new MouseMoveEvent(null), new MouseMoveEvent(null),
new RerenderScreen( new RerenderScreen(
new RerenderScreen.ScreenPart[]{ new RerenderScreen.ScreenPart[]{

View File

@@ -62,10 +62,10 @@ public class RerenderPartHandler extends AbstractEventHandler<RerenderPart> {
currentRoom, currentRoom,
room, room,
gameState.getPlayer(), gameState.getPlayer(),
gameState.getPlayer().getTexture(resourceManager),
screenBuffer, screenBuffer,
resourceManager, resourceManager,
debugging debugging,
gameState.getOtherPlayers()
); );
eventManager.emitEvent( eventManager.emitEvent(

View File

@@ -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);
}

View File

@@ -24,9 +24,16 @@ public class GameState {
@Setter @Setter
private Player player; private Player player;
@Getter
private final List<OtherPlayer> otherPlayers = new ArrayList<>(); private final List<OtherPlayer> otherPlayers = new ArrayList<>();
public List<OtherPlayer> getOtherPlayers() {
return otherPlayers.stream().filter(OtherPlayer::isVisible).toList();
}
public List<OtherPlayer> getAllOtherPlayers() {
return otherPlayers;
}
@Getter @Getter
@Setter @Setter
private Interactable interacting; private Interactable interacting;

View File

@@ -1,20 +1,41 @@
package cz.jzitnik.client.game; package cz.jzitnik.client.game;
import cz.jzitnik.client.game.mobs.HittableMob;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.awt.image.BufferedImage;
@Getter @Getter
public class OtherPlayer { public class OtherPlayer implements GamePlayer {
private final int id; private final int id;
private boolean hitAnimationOn = false; private boolean hitAnimationOn = false;
private final RoomCords playerCords; private final RoomCords playerCords;
@Setter @Setter
private Player.PlayerRotation playerRotation = Player.PlayerRotation.FRONT; private PlayerRotation playerRotation = PlayerRotation.FRONT;
@Setter
private boolean visible;
public OtherPlayer(PlayerCreation playerCreation) { public OtherPlayer(PlayerCreation playerCreation) {
this.id = playerCreation.getId(); this.id = playerCreation.getId();
this.playerCords = playerCreation.getPlayerCords(); 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;
}
} }

View File

@@ -11,6 +11,7 @@ import cz.jzitnik.client.utils.DependencyManager;
import cz.jzitnik.client.utils.events.Event; import cz.jzitnik.client.utils.events.Event;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -23,7 +24,7 @@ import java.util.concurrent.TimeUnit;
@Getter @Getter
@Slf4j @Slf4j
public class Player { public class Player implements GamePlayer {
private final int id; private final int id;
public static final int MAX_STAMINA = 20; public static final int MAX_STAMINA = 20;
public static final int MAX_HEALTH = 30; public static final int MAX_HEALTH = 30;
@@ -155,7 +156,4 @@ public class Player {
}, delayMs, TimeUnit.MILLISECONDS); }, delayMs, TimeUnit.MILLISECONDS);
} }
public enum PlayerRotation {
FRONT, BACK, LEFT, RIGHT
}
} }

View File

@@ -96,7 +96,7 @@ public class MobFollowingPlayerTask extends MobRoomTask {
Player player = gameState.getPlayer(); Player player = gameState.getPlayer();
BufferedImage playerTexture = player.getTexture(resourceManager); 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[]{ eventManager.emitEvent(new Event[]{
new MouseMoveEvent(null), new MouseMoveEvent(null),

View File

@@ -101,7 +101,6 @@ public final class Chest extends GameObject implements UIClickHandler {
Player player = gameState.getPlayer(); Player player = gameState.getPlayer();
BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture()); BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture());
BufferedImage playerTexture = player.getTexture(resourceManager);
BufferedImage chestTexture = getTexture(); BufferedImage chestTexture = getTexture();
var buffer = screenBuffer.getRenderedBuffer(); var buffer = screenBuffer.getRenderedBuffer();
@@ -141,7 +140,7 @@ public final class Chest extends GameObject implements UIClickHandler {
renderMaxX = Math.max(renderMaxX, prevDisplayStartX + prevWidth); renderMaxX = Math.max(renderMaxX, prevDisplayStartX + prevWidth);
clearPreviousUI( clearPreviousUI(
currentRoom, roomTexture, player, playerTexture, currentRoom, roomTexture, player, resourceManager,
buffer, overrideBuffer, start, buffer, overrideBuffer, start,
guiStartY, guiStartY,
prevGuiStartX, prevGuiStartX,
@@ -171,7 +170,7 @@ public final class Chest extends GameObject implements UIClickHandler {
GameRoom room, GameRoom room,
BufferedImage roomTexture, BufferedImage roomTexture,
Player player, Player player,
BufferedImage playerTexture, ResourceManager resourceManager,
Pixel[][] buffer, Pixel[][] buffer,
Pixel[][] overrideBuffer, Pixel[][] overrideBuffer,
RoomCords start, RoomCords start,
@@ -188,10 +187,11 @@ public final class Chest extends GameObject implements UIClickHandler {
null, null,
new HashSet<>(), new HashSet<>(),
player, player,
playerTexture, resourceManager,
x, x,
y, y,
debugging debugging,
gameState.getOtherPlayers()
).pixel(); ).pixel();
buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel); buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel);

View File

@@ -3,10 +3,12 @@ package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler; import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectDependency; import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState; 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.GameState;
import cz.jzitnik.client.game.OtherPlayer;
import cz.jzitnik.client.game.Player; import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.socket.AbstractSocketEventHandler; import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.states.TerminalState;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse; import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -18,6 +20,8 @@ public class ConnectGameHandler extends AbstractSocketEventHandler<ConnectToAGam
private GameState gameState; private GameState gameState;
@InjectDependency @InjectDependency
private EventManager eventManager; private EventManager eventManager;
@InjectState
private TerminalState terminalState;
@Override @Override
public void handle(ConnectToAGameResponse event) { public void handle(ConnectToAGameResponse event) {
@@ -27,7 +31,8 @@ public class ConnectGameHandler extends AbstractSocketEventHandler<ConnectToAGam
} }
gameState.setPlayer(new Player(event.playerCreation())); gameState.setPlayer(new Player(event.playerCreation()));
gameState.getAllOtherPlayers().addAll(event.existingPlayers().stream().map(OtherPlayer::new).toList());
gameState.setScreen(null); gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw()); eventManager.emitEvent(new TerminalResizeEvent(terminalState.getTerminalScreen().getTerminalSize()));
} }
} }

View File

@@ -0,0 +1,24 @@
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.PlayerArrivalChange;
@SocketEventHandler(PlayerArrivalChange.class)
public class PlayerArrivalChangeHandler extends AbstractSocketEventHandler<PlayerArrivalChange> {
@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());
}
}
}
}

View File

@@ -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<PlayerJoined> {
@InjectState
private GameState gameState;
@Override
public void handle(PlayerJoined event) {
log.debug("Player joined: {}", event.playerCreation().getId());
gameState.getAllOtherPlayers().add(new OtherPlayer(event.playerCreation()));
}
}

View File

@@ -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<PlayerMovedInUrRoom> {
@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)
)
}
)
});
}
}

View File

@@ -4,9 +4,7 @@ import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor; import com.googlecode.lanterna.TextColor;
import cz.jzitnik.client.config.Debugging; import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.events.handlers.FullRoomDrawHandler; import cz.jzitnik.client.events.handlers.FullRoomDrawHandler;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.*;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.game.mobs.Mob; import cz.jzitnik.client.game.mobs.Mob;
import cz.jzitnik.client.game.objects.DroppedItem; import cz.jzitnik.client.game.objects.DroppedItem;
import cz.jzitnik.client.game.objects.GameObject; import cz.jzitnik.client.game.objects.GameObject;
@@ -19,8 +17,11 @@ import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j @Slf4j
public class RerenderUtils { public class RerenderUtils {
@@ -36,14 +37,14 @@ public class RerenderUtils {
return new RoomCords(x, y); 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<OtherPlayer> otherPlayers) {
var buffer = screenBuffer.getRenderedBuffer(); var buffer = screenBuffer.getRenderedBuffer();
var overrideBuffer = currentRoom.getOverrideBuffer(); var overrideBuffer = currentRoom.getOverrideBuffer();
BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS); BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS);
Set<FullRoomDrawHandler.DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom); Set<FullRoomDrawHandler.DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom);
for (int x = startX; x <= endX; x++) { for (int x = startX; x <= endX; x++) {
for (int y = startY; y <= endY; y++) { 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 pixel = pixelResult.pixel();
int red = (pixel >> 16) & 0xff; int red = (pixel >> 16) & 0xff;
@@ -63,16 +64,23 @@ public class RerenderUtils {
public record PixelResult(int pixel, boolean isPlayer) { public record PixelResult(int pixel, boolean isPlayer) {
} }
public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set<FullRoomDrawHandler.DoorPosition> doorPositions, Player player, BufferedImage playerTexture, int x, int y, Debugging debugging) { public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set<FullRoomDrawHandler.DoorPosition> doorPositions, Player player, ResourceManager resourceManager, int x, int y, Debugging debugging, List<OtherPlayer> visiblePlayer) {
float factor = 1.5f; // brightness multiplier float factor = 1.5f; // brightness multiplier
if (debugging.renderColliders() && currentRoom.getColliders().stream().anyMatch(collider -> collider.isWithin(new RoomCords(x, y)))) { if (debugging.renderColliders() && currentRoom.getColliders().stream().anyMatch(collider -> collider.isWithin(new RoomCords(x, y)))) {
return new PixelResult(0xFFFF0000, false); 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) { List<GamePlayer> players = Stream.concat(
int relativeX = x - player.getPlayerCords().getX(); Stream.of(player),
int relativeY = y - player.getPlayerCords().getY(); visiblePlayer.stream()
).toList();
var playerCollider = player.getCollider(); 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();
if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) { if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) {
return new PixelResult(0xFFFF0000, false); return new PixelResult(0xFFFF0000, false);
@@ -85,6 +93,7 @@ public class RerenderUtils {
return new PixelResult(pixel, true); return new PixelResult(pixel, true);
} }
} }
}
for (Mob object : currentRoom.getMobs()) { for (Mob object : currentRoom.getMobs()) {
RoomCords startObjectCords = object.getCords(); RoomCords startObjectCords = object.getCords();

View File

@@ -1,33 +1,33 @@
- id: "spawn" - id: "spawn"
texture: "ROOM1" texture: "ROOM1"
mobs: #mobs:
- type: "hittable_drops" # - type: "hittable_drops"
texture: "PLAYER_FRONT" # texture: "PLAYER_FRONT"
cords: # cords:
x: 100 # x: 100
y: 100 # y: 100
collider: # collider:
start: # start:
x: 0 # x: 0
y: 52 # y: 52
end: # end:
x: 44 # x: 44
y: 78 # y: 78
health: 10 # health: 10
itemsDrops: # itemsDrops:
- name: "Apple" # - name: "Apple"
type: # type:
name: "food" # name: "food"
addHealth: 1 # addHealth: 1
texture: "APPLE" # texture: "APPLE"
tasks: # tasks:
- type: "blind_following_player" # - type: "blind_following_player"
speed: 1 # speed: 1
updateRateMs: 100 # updateRateMs: 100
- type: "attacking_player" # - type: "attacking_player"
damage: 5 # damage: 5
reach: 15 # reach: 15
updateRateMs: 500 # updateRateMs: 500
west: "empty" west: "empty"
east: "truhlaright" east: "truhlaright"
north: null north: null

View File

@@ -3,6 +3,8 @@ package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGame; import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGame;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse; 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.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext; import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler; import cz.jzitnik.server.events.AbstractEventHandler;
@@ -35,12 +37,23 @@ public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame>
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class); ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class);
PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml")); 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.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); game.getPlayers().add(client);
client.getSession().sendMessage(new ConnectToAGameResponse(player));
} }
} }

View File

@@ -35,7 +35,7 @@ public class CreateGameHandler extends AbstractEventHandler<CreateGame> {
PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml")); PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml"));
player.setId(id); player.setId(id);
client.setPlayer(new Player(id, player.getPlayerCords())); client.setPlayer(new Player(player));
CreateGameResponse gameResponse = new CreateGameResponse(pass, player); CreateGameResponse gameResponse = new CreateGameResponse(pass, player);
Game game = new Game( Game game = new Game(

View File

@@ -19,7 +19,7 @@ public class PlayerMoveHandler extends AbstractEventHandler<PlayerMove> {
for (Client player : client.getGame().getPlayers()) { for (Client player : client.getGame().getPlayers()) {
if (player.getPlayer().getCurrentRoom().equals(client.getPlayer().getCurrentRoom()) && player.getPlayer().getId() != client.getPlayer().getId()) { 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()));
} }
} }
} }

View File

@@ -1,15 +1,23 @@
package cz.jzitnik.server.game; package cz.jzitnik.server.game;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.player.PlayerCreation;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
@Getter @Getter
@RequiredArgsConstructor
public class Player { public class Player {
private final int id; private final int id;
private final RoomCords cords; private final RoomCords cords;
@Setter @Setter
private String currentRoom; private String currentRoom;
public Player(PlayerCreation creation) {
id = creation.getId();
cords = creation.getPlayerCords();
}
public PlayerCreation toPlayerCreation() {
return new PlayerCreation(cords, null);
}
} }