Compare commits

..

23 Commits

Author SHA1 Message Date
f6de75afd9 feat: Store player rotation 2026-02-03 09:54:23 +01:00
1f54e933ad chore: Remove todo 2026-02-02 22:04:54 +01:00
9ca3a8d34c feat: Multiplayer player moving between rooms 2026-02-02 21:59:55 +01:00
e3f5c12b43 feat: Player movement multiplayer 2026-02-02 21:06:46 +01:00
24f54b8b7a feat: Connecting to a game 2026-02-02 19:18:25 +01:00
92eb93815d feat: Some more multiplayer stuff
Hello miss Meitnerova... If you are not Meitnerova but some random
person stalking my projects go get a life.

Anyways have a great time reading this awful code :)
2026-02-02 18:51:51 +01:00
f987b2bed6 chore: Add logs to gitignore 2026-02-01 15:23:37 +01:00
b79c2f3373 chore: Remove logs 2026-02-01 15:23:20 +01:00
ac5a96eb19 feat: Server selector 2026-02-01 15:16:44 +01:00
fc14e4081d feat: Finallized communication 2026-01-31 19:46:33 +01:00
ef14edffde feat: Started basic implementation of socket comm 2026-01-31 17:02:24 +01:00
bfcce054d5 chore: Starting setting up multiplayer 2026-01-30 13:14:56 +01:00
b72ac87098 chore: Game 2026-01-25 16:15:12 +01:00
5193b4aba8 chore: Updated dependencies 2026-01-24 22:06:20 +01:00
6e665dbbdd refactor: Config in files 2026-01-24 21:40:44 +01:00
743ad8e760 refactor: Load a game setup from a yaml file 2026-01-24 21:08:43 +01:00
aac651cf93 refactor: Reworked collision system 2026-01-22 16:11:16 +01:00
f8d28acc33 feat: Dialog running code and changeable tasks 2026-01-22 14:13:41 +01:00
46981937ce refactor(events): Small event manager refactoring 2026-01-21 08:05:32 +01:00
cb488f9853 feat: Show icons in stats 2026-01-20 12:36:18 +01:00
da45765413 feat: Dialog answering 2026-01-19 20:17:13 +01:00
eef269c853 feat: Implemented text rendering 2026-01-19 17:20:49 +01:00
6335ab7e5c chore: Started implementing dialog 2026-01-19 09:30:50 +01:00
56 changed files with 316 additions and 1232 deletions

1
.idea/compiler.xml generated
View File

@@ -34,7 +34,6 @@
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" /> <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" />
</processorPath> </processorPath>
<module name="game (1)" /> <module name="game (1)" />
<module name="game" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
</component> </component>

View File

@@ -1,37 +0,0 @@
# Terminal Game
A multiplayer terminal-based game built with Java, utilizing WebSockets for communication and Lanterna for the text-based user interface.
## Project Structure
* **game**: Client application (TUI).
* **server**: WebSocket server.
* **common**: Shared libraries and logic.
## Requirements
* Java 25
* Maven
## How to Run
1. Build the project:
```bash
mvn clean install
```
2. Start the server:
```bash
mvn compile exec:java -pl server -am
```
3. Start the client (in a new terminal):
```bash
mvn compile exec:java -pl game -am
```
## Controls
* **Left Click**: Interact with objects and fight.
* **WASD**: Move the character.
* **CTRL**: Hold to run (sprint).

View File

@@ -6,7 +6,6 @@ import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
@@ -17,13 +16,6 @@ public class RoomCords implements Cloneable, Serializable {
private int x; private int x;
private int y; private int y;
public RoomCords calculateCenter(BufferedImage texture) {
return new RoomCords(
x + texture.getWidth() / 2,
y + texture.getHeight() / 2
);
}
@JsonCreator @JsonCreator
public RoomCords( public RoomCords(
@JsonProperty("x") int x, @JsonProperty("x") int x,

View File

@@ -1,6 +0,0 @@
package cz.jzitnik.common.socket.messages.game;
import cz.jzitnik.common.socket.SocketMessage;
public record GameWin() implements SocketMessage {
}

View File

@@ -1,6 +0,0 @@
package cz.jzitnik.common.socket.messages.game;
import cz.jzitnik.common.socket.SocketMessage;
public record PlayerDeath(int playerId) implements SocketMessage {
}

View File

@@ -1,9 +0,0 @@
package cz.jzitnik.common.socket.messages.items;
import cz.jzitnik.common.socket.SocketMessage;
public record ItemTookFromChest(
String roomId, // For faster lookup i guess
int id
) implements SocketMessage {
}

View File

@@ -3,6 +3,6 @@ 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 PlayerArrivalChange(int id, RoomCords playerCords, PlayerRotation playerRotation, public record PlayerArrivalChange(int id, RoomCords playerCords, boolean arrived,
boolean arrived, boolean rerender) implements SocketMessage { boolean rerender) implements SocketMessage {
} }

View File

@@ -1,6 +0,0 @@
package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.socket.SocketMessage;
public record PlayerDisconnected(int playerId) implements SocketMessage {
}

View File

@@ -7,7 +7,6 @@ import cz.jzitnik.client.annotations.EventHandler;
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.RerenderScreen; import cz.jzitnik.client.events.RerenderScreen;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.dialog.Dialog; import cz.jzitnik.client.game.dialog.Dialog;
import cz.jzitnik.client.game.dialog.OnEnd; import cz.jzitnik.client.game.dialog.OnEnd;
import cz.jzitnik.client.states.DialogState; import cz.jzitnik.client.states.DialogState;
@@ -23,8 +22,8 @@ import cz.jzitnik.client.utils.events.EventManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ArrayList;
@Slf4j @Slf4j
@EventHandler(Dialog.class) @EventHandler(Dialog.class)
@@ -39,9 +38,6 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
@InjectState @InjectState
private ScreenBuffer screenBuffer; private ScreenBuffer screenBuffer;
@InjectState
private GameState gameState;
@InjectDependency @InjectDependency
private EventManager eventManager; private EventManager eventManager;
@@ -60,45 +56,32 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
public static final int BUTTON_PADDING = 5; public static final int BUTTON_PADDING = 5;
private static final float FONT_SIZE = 15f; private static final float FONT_SIZE = 15f;
public static int calculateButtonHeight(Dialog dialog, GameState gameState) { public static int calculateButtonHeight(Dialog dialog) {
if (dialog.getOnEnd() instanceof OnEnd.AskQuestion askQuestion) { if (dialog.getOnEnd() instanceof OnEnd.AskQuestion(OnEnd.AskQuestion.Answer[] answers)) {
int count = askQuestion.answers(gameState).length; return answers.length * BUTTON_HEIGHT + (answers.length - 1) * BUTTON_PADDING;
return count * BUTTON_HEIGHT + Math.max(0, count - 1) * BUTTON_PADDING; } else {
}
return 0; return 0;
} }
}
public static int getYStartButtons(TextRenderer textRenderer, Dialog dialog) { public static int getYStartButtons(TextRenderer textRenderer, Dialog dialog) {
var textSize = textRenderer.measureText( var textSize = textRenderer.measureText(dialog.getText(), WIDTH - PADDING * 2, FONT_SIZE);
dialog.getText(),
WIDTH - PADDING * 2,
FONT_SIZE
);
return PADDING + textSize.height + BUTTON_PADDING; return PADDING + textSize.height + BUTTON_PADDING;
} }
public static TerminalSize getSize(TextRenderer textRenderer, Dialog dialog, GameState gameState) { public static TerminalSize getSize(TextRenderer textRenderer, Dialog dialog) {
var textSize = textRenderer.measureText( var textSize = textRenderer.measureText(dialog.getText(), WIDTH - PADDING * 2, FONT_SIZE);
dialog.getText(),
WIDTH - PADDING * 2,
FONT_SIZE
);
int buttonsHeight = 0; return new TerminalSize(300, PADDING + textSize.height + (
if (dialog.getOnEnd() instanceof OnEnd.AskQuestion) { dialog.getOnEnd() instanceof OnEnd.AskQuestion ? BUTTON_PADDING + calculateButtonHeight(dialog) : 0
buttonsHeight = BUTTON_PADDING + calculateButtonHeight(dialog, gameState); ) + PADDING);
}
return new TerminalSize(
300,
PADDING + textSize.height + buttonsHeight + PADDING
);
} }
public static TerminalPosition getStart(TerminalSize terminalSize, TerminalSize size) { public static TerminalPosition getStart(TerminalSize terminalSize, TerminalSize size) {
int startY = terminalSize.getRows() * 2 - MARGIN_BOTTOM - size.getRows(); int startY = terminalSize.getRows() * 2 - MARGIN_BOTTOM - size.getRows();
int startX = (terminalSize.getColumns() / 2) - (size.getColumns() / 2); int startX = (terminalSize.getColumns() / 2) - (size.getColumns() / 2);
return new TerminalPosition(startX, startY); return new TerminalPosition(startX, startY);
} }
@@ -106,114 +89,58 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
public void handle(Dialog event) { public void handle(Dialog event) {
boolean onlyLast = dialogState.getCurrentDialog() == event; boolean onlyLast = dialogState.getCurrentDialog() == event;
dialogState.setCurrentDialog(event); dialogState.setCurrentDialog(event);
TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize(); TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize();
var overrideBuffer = screenBuffer.getGlobalOverrideBuffer(); var overrideBuffer = screenBuffer.getGlobalOverrideBuffer();
var size = getSize(textRenderer, event, gameState); var size = getSize(textRenderer, event);
var start = getStart(terminalSize, size); var start = getStart(terminalSize, size);
var animation = textRenderer.renderTypingAnimation( var animation = textRenderer.renderTypingAnimation(event.getText(), size.getColumns() - PADDING * 2, size.getRows() - PADDING * 2, Color.WHITE, FONT_SIZE);
event.getText(), var textSize = textRenderer.measureText(event.getText(), size.getColumns() - PADDING * 2, FONT_SIZE);
size.getColumns() - PADDING * 2,
size.getRows() - PADDING * 2,
Color.WHITE,
FONT_SIZE
);
var textSize = textRenderer.measureText(
event.getText(),
size.getColumns() - PADDING * 2,
FONT_SIZE
);
OnEnd onEnd = event.getOnEnd(); OnEnd onEnd = event.getOnEnd();
List<AlphaPixel[][]> answersBuf = new ArrayList<>(); List<AlphaPixel[][]> answersBuf = new ArrayList<>();
OnEnd.AskQuestion askQuestion = null;
if (onEnd instanceof OnEnd.AskQuestion aq) { if (onEnd instanceof OnEnd.AskQuestion(
askQuestion = aq; OnEnd.AskQuestion.Answer[] answers
for (OnEnd.AskQuestion.Answer answer : aq.answers(gameState)) { )) {
answersBuf.add( for (OnEnd.AskQuestion.Answer answer : answers) {
textRenderer.renderText( answersBuf.add(textRenderer.renderText(answer.answer(), size.getColumns() - PADDING * 2, BUTTON_HEIGHT, Color.BLACK, FONT_SIZE, false));
answer.answer(),
size.getColumns() - PADDING * 2,
BUTTON_HEIGHT,
Color.BLACK,
FONT_SIZE,
false
)
);
} }
} }
dialogState.setRenderInProgress(true); dialogState.setRenderInProgress(true);
try { try {
for (int i = onlyLast ? animation.length : 0; i <= animation.length; i++) { for (int i = onlyLast ? animation.length : 0; i <= animation.length; i++) {
var buf = animation[Math.min(i, animation.length - 1)]; var buf = animation[Math.min(i, animation.length - 1)];
for (int y = 0; y < size.getRows(); y++) { for (int y = 0; y < size.getRows(); y++) {
for (int x = 0; x < size.getColumns(); x++) { for (int x = 0; x < size.getColumns(); x++) {
var textPixel = buf[Math.min(Math.max(0, y - PADDING), buf.length - 1)][Math.min(Math.max(0, x - PADDING), buf[0].length - 1)];
var textPixel = buf[ if (textPixel instanceof Empty || y < PADDING || x < PADDING || x >= size.getColumns() - PADDING || y >= size.getRows() - PADDING) {
Math.min(Math.max(0, y - PADDING), buf.length - 1) if (i == animation.length && y - 2 > textSize.height + QUESTION_ACTIONS_GAP && onEnd instanceof OnEnd.AskQuestion(
][ OnEnd.AskQuestion.Answer[] answers
Math.min(Math.max(0, x - PADDING), buf[0].length - 1) )) {
];
if (textPixel instanceof Empty
|| y < PADDING
|| x < PADDING
|| x >= size.getColumns() - PADDING
|| y >= size.getRows() - PADDING) {
if (i == animation.length
&& askQuestion != null
&& y - 2 > textSize.height + QUESTION_ACTIONS_GAP) {
var answers = askQuestion.answers(gameState);
int buttonsY = y - textSize.height - QUESTION_ACTIONS_GAP - 2; int buttonsY = y - textSize.height - QUESTION_ACTIONS_GAP - 2;
int buttonIndex = buttonsY / (BUTTON_HEIGHT + BUTTON_PADDING); int buttonIndex = buttonsY / (BUTTON_HEIGHT + BUTTON_PADDING);
int rest = buttonsY % (BUTTON_HEIGHT + BUTTON_PADDING); int rest = buttonsY % (BUTTON_HEIGHT + BUTTON_PADDING);
if (buttonIndex < answers.length if (buttonIndex < answers.length && rest < BUTTON_HEIGHT && x >= PADDING && x < size.getColumns() - PADDING) {
&& rest < BUTTON_HEIGHT
&& x >= PADDING
&& x < size.getColumns() - PADDING) {
int localY = rest - BUTTON_TEXT_PADDING; int localY = rest - BUTTON_TEXT_PADDING;
int localX = x - PADDING - BUTTON_TEXT_PADDING; int localX = x - PADDING - BUTTON_TEXT_PADDING;
var buttonBuf = answersBuf.get(buttonIndex); var buttonBuf = answersBuf.get(buttonIndex);
var buttonTextPixel = buttonBuf[ var buttonTextPixel = buttonBuf[Math.min(Math.max(0, localY), buttonBuf.length - 1)][Math.min(Math.max(0, localX), buttonBuf[0].length - 1)];
Math.min(Math.max(0, localY), buttonBuf.length - 1)
][
Math.min(Math.max(0, localX), buttonBuf[0].length - 1)
];
if (buttonTextPixel instanceof Empty if (buttonTextPixel instanceof Empty || localY < 0 || localX < 0 || localY >= buttonBuf.length || localX >= buttonBuf[0].length) {
|| localY < 0 overrideBuffer[start.getRow() + y][start.getColumn() + x] = new ColoredPixel(new TextColor.RGB(255, 255, 255), dialogState.getHoveredButtonIndex() == buttonIndex ? 0.8f : 0.6f);
|| localX < 0
|| localY >= buttonBuf.length
|| localX >= buttonBuf[0].length) {
overrideBuffer[start.getRow() + y][start.getColumn() + x] =
new ColoredPixel(
new TextColor.RGB(255, 255, 255),
dialogState.getHoveredButtonIndex() == buttonIndex ? 0.8f : 0.6f
);
} else { } else {
overrideBuffer[start.getRow() + y][start.getColumn() + x] = overrideBuffer[start.getRow() + y][start.getColumn() + x] = buttonTextPixel;
buttonTextPixel;
} }
continue; continue;
} }
} }
overrideBuffer[start.getRow() + y][start.getColumn() + x] = overrideBuffer[start.getRow() + y][start.getColumn() + x] = new ColoredPixel(new TextColor.RGB(0, 0, 0), 0.6f);
new ColoredPixel(new TextColor.RGB(0, 0, 0), 0.6f);
continue; continue;
} }
@@ -225,10 +152,7 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
new RerenderScreen( new RerenderScreen(
new RerenderScreen.ScreenPart( new RerenderScreen.ScreenPart(
start, start,
new TerminalPosition( new TerminalPosition(start.getColumn() + size.getColumns(), start.getRow() + size.getRows())
start.getColumn() + size.getColumns(),
start.getRow() + size.getRows()
)
) )
) )
); );
@@ -237,6 +161,7 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
} }
dialogState.setRenderInProgress(false); dialogState.setRenderInProgress(false);
next(onEnd, start, size); next(onEnd, start, size);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -245,39 +170,31 @@ public class DialogEventHandler extends AbstractEventHandler<Dialog> {
} }
private void next(OnEnd onEnd, TerminalPosition start, TerminalSize size) throws InterruptedException { private void next(OnEnd onEnd, TerminalPosition start, TerminalSize size) throws InterruptedException {
Thread.sleep(1000);
if (onEnd instanceof OnEnd.Continue(Dialog nextDialog)) { if (onEnd instanceof OnEnd.Continue(Dialog nextDialog)) {
clear(start, size); Thread.sleep(1000);
eventManager.emitEvent(nextDialog);
} else if (onEnd instanceof OnEnd.RunCode runCode) {
Runnable runnable = runCode.getRunnable();
dependencyManager.inject(runnable);
runnable.run();
next(runCode.getOnEnd(), start, size);
} else if (onEnd instanceof OnEnd.End) {
clear(start, size);
dialogState.setCurrentDialog(null);
eventManager.emitEvent(
new RerenderScreen(
new RerenderScreen.ScreenPart(
start,
new TerminalPosition(
start.getColumn() + size.getColumns(),
start.getRow() + size.getRows()
)
)
)
);
}
}
private void clear(TerminalPosition start, TerminalSize size) {
for (int y = start.getRow(); y < start.getRow() + size.getRows(); y++) { for (int y = start.getRow(); y < start.getRow() + size.getRows(); y++) {
for (int x = start.getColumn(); x < start.getColumn() + size.getColumns(); x++) { for (int x = start.getColumn(); x < start.getColumn() + size.getColumns(); x++) {
screenBuffer.getGlobalOverrideBuffer()[y][x] = new Empty(); screenBuffer.getGlobalOverrideBuffer()[y][x] = new Empty();
} }
} }
if (nextDialog == null) {
dialogState.setCurrentDialog(null);
eventManager.emitEvent(
new RerenderScreen(
new RerenderScreen.ScreenPart(
start,
new TerminalPosition(start.getColumn() + size.getColumns(), start.getRow() + size.getRows())
)
)
);
} else {
eventManager.emitEvent(nextDialog);
}
} else if (onEnd instanceof OnEnd.RunCode(Runnable runnable, OnEnd end)) {
dependencyManager.inject(runnable);
runnable.run();
next(end, start, size);
}
} }
} }

View File

@@ -7,16 +7,13 @@ import cz.jzitnik.client.events.RoomChangeEvent;
import cz.jzitnik.client.events.SendSocketMessageEvent; import cz.jzitnik.client.events.SendSocketMessageEvent;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Requirement;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.utils.events.AbstractEventHandler; import cz.jzitnik.client.utils.events.AbstractEventHandler;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler; import cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler;
import cz.jzitnik.common.socket.messages.game.GameWin;
import cz.jzitnik.common.socket.messages.room.MovePlayerRoom; import cz.jzitnik.common.socket.messages.room.MovePlayerRoom;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -48,20 +45,6 @@ public class RoomChangeEventHandler extends AbstractEventHandler<RoomChangeEvent
return; return;
} }
if (newRoom.getRequirement() != null) {
Requirement requirement = newRoom.getRequirement();
String itemType = requirement.itemType();
if (Arrays.stream(gameState.getPlayer().getInventory()).noneMatch(item -> {
if (item == null) {
return false;
}
return item.getType().getItemType().getSimpleName().equals(itemType);
})) {
return;
}
}
switch (event.door()) { switch (event.door()) {
case LEFT -> playerCords.updateCords(155, playerCords.getY()); case LEFT -> playerCords.updateCords(155, playerCords.getY());
case RIGHT -> playerCords.updateCords(30, playerCords.getY()); case RIGHT -> playerCords.updateCords(30, playerCords.getY());
@@ -72,10 +55,6 @@ public class RoomChangeEventHandler extends AbstractEventHandler<RoomChangeEvent
eventManager.emitEvent(new SendSocketMessageEvent(new MovePlayerRoom(newRoom.getId(), oldCords, playerCords))); eventManager.emitEvent(new SendSocketMessageEvent(new MovePlayerRoom(newRoom.getId(), oldCords, playerCords)));
gameState.setCurrentRoom(newRoom); gameState.setCurrentRoom(newRoom);
if (newRoom.isEnd()) {
eventManager.emitEvent(new SendSocketMessageEvent(new GameWin()));
} else {
scheduler.schedule(() -> roomTaskScheduler.setupNewSchedulers(newRoom), 200, TimeUnit.MILLISECONDS); scheduler.schedule(() -> roomTaskScheduler.setupNewSchedulers(newRoom), 200, TimeUnit.MILLISECONDS);
} }
} }
}

View File

@@ -34,12 +34,6 @@ public class GameRoom {
@JsonIgnore @JsonIgnore
private final List<RoomPart> colliders = new ArrayList<>(); private final List<RoomPart> colliders = new ArrayList<>();
@JsonProperty("requirement")
private Requirement requirement;
@JsonProperty("end")
private boolean end;
private GameRoom left; private GameRoom left;
private GameRoom right; private GameRoom right;
private GameRoom up; private GameRoom up;

View File

@@ -20,10 +20,6 @@ public class GameState {
@Setter @Setter
private GameRoom currentRoom; private GameRoom currentRoom;
@Getter
@Setter
private List<GameRoom> allRooms;
@Getter @Getter
@Setter @Setter
private Player player; private Player player;

View File

@@ -28,7 +28,7 @@ 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;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final RoomCords playerCords; private final RoomCords playerCords;
private final RoomPart collider; private final RoomPart collider;
@@ -64,8 +64,6 @@ public class Player implements GamePlayer {
public boolean dealDamage(int amount, DependencyManager dependencyManager) { public boolean dealDamage(int amount, DependencyManager dependencyManager) {
if (health - amount <= 0) { if (health - amount <= 0) {
health = 0; health = 0;
EventManager eventManager = dependencyManager.getDependencyOrThrow(EventManager.class);
eventManager.emitEvent(new cz.jzitnik.client.events.SendSocketMessageEvent(new cz.jzitnik.common.socket.messages.game.PlayerDeath(id)));
return true; return true;
} }

View File

@@ -1,13 +0,0 @@
package cz.jzitnik.client.game;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public record Requirement(String itemType) {
@JsonCreator
public Requirement(
@JsonProperty("item") String itemType
) {
this.itemType = itemType;
}
}

View File

@@ -23,14 +23,6 @@ public class ResourceManager {
ROOM2("rooms/2.png"), ROOM2("rooms/2.png"),
ROOM3("rooms/3.png"), ROOM3("rooms/3.png"),
ROOM4("rooms/4.png"), ROOM4("rooms/4.png"),
ROOM5("rooms/5.png"),
ROOM6("rooms/6.png"),
ROOM7("rooms/7.png"),
ROOM8("rooms/8.png"),
ROOM9("rooms/9.png"),
ROOM10("rooms/10.png"),
ROOM11("rooms/11.png"),
ROOM12("rooms/12.png"),
ROOM_FROZEN("rooms/frozen.png"), ROOM_FROZEN("rooms/frozen.png"),
PLAYER_FRONT("player/front.png"), PLAYER_FRONT("player/front.png"),
@@ -44,23 +36,8 @@ public class ResourceManager {
APPLE("food/apple.png"), APPLE("food/apple.png"),
// TEMP TEXTURES JUST TO GET THE GAME WORKING
OLD_MAN("player/front.png"),
KEY_KEEPER("player/front.png"),
CAVE_BEAST("player/front.png"),
BLIND_HUNTER("player/front.png"),
ZOMBIE("player/front.png"),
RUSTY_SWORD("tools/wooden_sword.png"),
AXE("tools/wooden_sword.png"),
DAGGER("tools/wooden_sword.png"),
BREAD("food/apple.png"),
ROCK("food/apple.png"),
KEY("food/apple.png"),
BOSS_SKIN("tools/wooden_sword.png"),
// UI
DOORS("rooms/doors.png"), DOORS("rooms/doors.png"),
STAMINA("ui/stamina.png"), STAMINA("ui/stamina.png"),
HEART("ui/heart.png"); HEART("ui/heart.png");

View File

@@ -1,25 +1,18 @@
package cz.jzitnik.client.game.dialog; package cz.jzitnik.client.game.dialog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.client.utils.events.Event; import cz.jzitnik.client.utils.events.Event;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
@AllArgsConstructor
@RequiredArgsConstructor
@Getter @Getter
public class Dialog implements Event { public class Dialog implements Event {
/** /**
* Characters per second * Characters per second
*/ */
private final int typingSpeed = 10; private int typingSpeed = 10;
private final String text; private final String text;
private final OnEnd onEnd; private final OnEnd onEnd;
@JsonCreator
public Dialog(
@JsonProperty("text") String text,
@JsonProperty("onEnd") OnEnd onEnd
) {
this.text = text;
this.onEnd = onEnd;
}
} }

View File

@@ -1,135 +1,13 @@
package cz.jzitnik.client.game.dialog; package cz.jzitnik.client.game.dialog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.InventoryRerender;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.game.Requirement;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.mobs.HittableMobDrops;
import cz.jzitnik.client.utils.events.EventManager;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Optional;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = OnEnd.Continue.class, name = "continue"),
@JsonSubTypes.Type(value = OnEnd.AskQuestion.class, name = "ask_question"),
@JsonSubTypes.Type(value = OnEnd.End.class, name = "end"),
@JsonSubTypes.Type(value = OnEnd.GiveItem.class, name = "give_item")
})
public interface OnEnd { public interface OnEnd {
record End() implements OnEnd { record RunCode(Runnable runnable, OnEnd onEnd) implements OnEnd {}
}
class GiveItem extends RunCode {
@JsonCreator
public GiveItem(
@JsonProperty("item") GameItem item,
@JsonProperty("then") OnEnd onEnd
) {
super(new Run(item), onEnd);
}
@RequiredArgsConstructor
private static class Run implements Runnable {
private final GameItem item;
@InjectState
private GameState gameState;
@InjectDependency
private EventManager eventManager;
@InjectDependency
private ResourceManager resourceManager;
@Override
public void run() {
Player player = gameState.getPlayer();
var playerCords = player.getPlayerCords().calculateCenter(player.getTexture(resourceManager));
boolean addedIntoInventory = player.addItem(item);
if (!addedIntoInventory) {
eventManager.emitEvent(HittableMobDrops.dropItem(playerCords.getX(), playerCords.getY(), gameState.getCurrentRoom(), item));
} else {
eventManager.emitEvent(new InventoryRerender());
}
}
}
}
@Getter
@RequiredArgsConstructor
class RunCode implements OnEnd {
private final Runnable runnable;
private final OnEnd onEnd;
}
record Continue(Dialog nextDialog) implements OnEnd { record Continue(Dialog nextDialog) implements OnEnd {
@JsonCreator
public Continue(@JsonProperty("nextDialog") Dialog nextDialog) {
this.nextDialog = nextDialog;
}
} }
record AskQuestion(Answer[] answers) implements OnEnd { record AskQuestion(Answer[] answers) implements OnEnd {
@JsonCreator public record Answer(String answer, Dialog dialog) {
public AskQuestion(@JsonProperty("answers") Answer[] answers) {
this.answers = answers;
}
public record Answer(
String answer,
Dialog dialog,
Optional<Requirement> requirement
) {
@JsonCreator
public Answer(
@JsonProperty("answer") String answer,
@JsonProperty("dialog") Dialog dialog,
@JsonProperty("requirement") Requirement requirement
) {
this(answer, dialog, Optional.ofNullable(requirement));
}
private boolean isValid(GameState gameState) {
if (requirement.isPresent()) {
Requirement requirement = requirement().get();
if (requirement.itemType() != null) {
return Arrays.stream(gameState.getPlayer().getInventory()).anyMatch(item -> {
if (item == null) {
return false;
}
return item.getType().getItemType().getSimpleName().equals(requirement.itemType());
});
}
return true;
}
return true;
}
}
public Answer[] answers(GameState gameState) {
return Arrays.stream(answers)
.filter(answer -> answer.isValid(gameState))
.toArray(Answer[]::new);
} }
} }
} }

View File

@@ -14,17 +14,14 @@ public class GameItem implements Renderable {
@JsonIgnore @JsonIgnore
private final BufferedImage texture; private final BufferedImage texture;
private final String name; private final String name;
private final int id;
@JsonCreator @JsonCreator
public GameItem( public GameItem(
@JsonProperty("id") int id,
@JsonProperty("name") String name, @JsonProperty("name") String name,
@JsonProperty("type") ItemType<?> type, @JsonProperty("type") ItemType<?> type,
@JsonProperty("texture") ResourceManager.Resource resource, @JsonProperty("texture") ResourceManager.Resource resource,
@JacksonInject ResourceManager resourceManager @JacksonInject ResourceManager resourceManager
) { ) {
this.id = id;
this.name = name; this.name = name;
this.type = type; this.type = type;
this.texture = resourceManager.getResource(resource); this.texture = resourceManager.getResource(resource);

View File

@@ -1,8 +0,0 @@
package cz.jzitnik.client.game.items.types;
public class BeastSkin implements ItemType<BeastSkin> {
@Override
public Class<BeastSkin> getItemType() {
return BeastSkin.class;
}
}

View File

@@ -11,10 +11,7 @@ import cz.jzitnik.client.game.items.types.weapons.Sword;
) )
@JsonSubTypes({ @JsonSubTypes({
@JsonSubTypes.Type(value = Food.class, name = "food"), @JsonSubTypes.Type(value = Food.class, name = "food"),
@JsonSubTypes.Type(value = Sword.class, name = "weapon_sword"), @JsonSubTypes.Type(value = Sword.class, name = "weapon_sword")
@JsonSubTypes.Type(value = Junk.class, name = "junk"),
@JsonSubTypes.Type(value = Key.class, name = "key"),
@JsonSubTypes.Type(value = BeastSkin.class, name = "beast_skin"),
}) })
public interface ItemType<T> { public interface ItemType<T> {
Class<T> getItemType(); Class<T> getItemType();

View File

@@ -1,8 +0,0 @@
package cz.jzitnik.client.game.items.types;
public class Junk implements ItemType<Junk> {
@Override
public Class<Junk> getItemType() {
return Junk.class;
}
}

View File

@@ -1,8 +0,0 @@
package cz.jzitnik.client.game.items.types;
public class Key implements ItemType<Key> {
@Override
public Class<Key> getItemType() {
return Key.class;
}
}

View File

@@ -1,11 +1,7 @@
package cz.jzitnik.client.game.mobs; package cz.jzitnik.client.game.mobs;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
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.game.ResourceManager;
import cz.jzitnik.common.models.coordinates.RoomPart; import cz.jzitnik.common.models.coordinates.RoomPart;
import cz.jzitnik.client.game.dialog.Dialog; import cz.jzitnik.client.game.dialog.Dialog;
import cz.jzitnik.client.game.mobs.tasks.MobRoomTask; import cz.jzitnik.client.game.mobs.tasks.MobRoomTask;
@@ -14,20 +10,14 @@ import cz.jzitnik.client.states.DialogState;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@Slf4j @Slf4j
public class DialogMob extends Mob { public abstract class DialogMob extends Mob {
protected Dialog dialog; protected Dialog dialog;
@JsonCreator public DialogMob(BufferedImage texture, MobRoomTask[] tasks, RoomCords cords, RoomPart collider, Dialog dialog) {
public DialogMob( super(texture, tasks, cords, collider);
@JsonProperty("texture") ResourceManager.Resource texture,
@JsonProperty("tasks") MobRoomTask[] tasks,
@JsonProperty("cords") RoomCords cords,
@JsonProperty("collider") RoomPart collider,
@JsonProperty("dialog") Dialog dialog,
@JacksonInject ResourceManager resourceManager
) {
super(resourceManager.getResource(texture), tasks, cords, collider);
this.dialog = dialog; this.dialog = dialog;
} }

View File

@@ -55,9 +55,10 @@ public class HittableMobDrops extends HittableMob {
Player player = gameState.getPlayer(); Player player = gameState.getPlayer();
RoomCords enemyCords = getCords(); RoomCords enemyCords = getCords();
BufferedImage enemyTexture = getTexture(); BufferedImage enemyTexture = getTexture();
GameRoom currentRoom = gameState.getCurrentRoom();
int roomX = enemyCords.getX() + enemyTexture.getWidth() / 2; int roomX = enemyCords.getX() + enemyTexture.getWidth() / 2;
int roomY = enemyCords.getY() + enemyTexture.getHeight() / 2; int roomY = enemyCords.getY() + enemyTexture.getHeight() / 2;
GameRoom currentRoom = gameState.getCurrentRoom();
List<Event> events = new ArrayList<>(); List<Event> events = new ArrayList<>();
@@ -70,21 +71,17 @@ public class HittableMobDrops extends HittableMob {
events.add(new InventoryRerender()); events.add(new InventoryRerender());
} }
} else { } else {
events.add(dropItem(roomX, roomY, currentRoom, item)); double angle = ThreadLocalRandom.current().nextDouble(0, Math.PI * 2);
double radius = ThreadLocalRandom.current().nextDouble(0, DROP_ITEM_ON_GROUND_RADIUS);
int randomX = roomX + (int) (Math.cos(angle) * radius);
int randomY = roomY + (int) (Math.sin(angle) * radius);
RoomCords itemCords = new RoomCords(randomX, randomY);
DroppedItem droppedItem = new DroppedItem(currentRoom, itemCords, item);
currentRoom.getDroppedItems().add(droppedItem);
events.add(new DroppedItemRerender(droppedItem));
} }
} }
eventManager.emitEvent(events, this::afterKill); eventManager.emitEvent(events, this::afterKill);
} }
public static Event dropItem(int x, int y, GameRoom currentRoom, GameItem item) {
double angle = ThreadLocalRandom.current().nextDouble(0, Math.PI * 2);
double radius = ThreadLocalRandom.current().nextDouble(0, DROP_ITEM_ON_GROUND_RADIUS);
int randomX = x + (int) (Math.cos(angle) * radius);
int randomY = y + (int) (Math.sin(angle) * radius);
RoomCords itemCords = new RoomCords(randomX, randomY);
DroppedItem droppedItem = new DroppedItem(currentRoom, itemCords, item);
currentRoom.getDroppedItems().add(droppedItem);
return new DroppedItemRerender(droppedItem);
}
} }

View File

@@ -10,7 +10,10 @@ import cz.jzitnik.client.annotations.injectors.InjectConfig;
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.config.Debugging; import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.events.*; import cz.jzitnik.client.events.InventoryRerender;
import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.events.RerenderPart;
import cz.jzitnik.client.events.RerenderScreen;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player; import cz.jzitnik.client.game.Player;
@@ -27,12 +30,9 @@ import cz.jzitnik.client.ui.pixels.Pixel;
import cz.jzitnik.client.utils.RerenderUtils; import cz.jzitnik.client.utils.RerenderUtils;
import cz.jzitnik.client.utils.UIRoomClickHandlerRepository; import cz.jzitnik.client.utils.UIRoomClickHandlerRepository;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.net.Socket;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -40,11 +40,8 @@ import java.util.List;
public final class Chest extends GameObject implements UIClickHandler { public final class Chest extends GameObject implements UIClickHandler {
private static final int RENDER_PADDING = 1; private static final int RENDER_PADDING = 1;
@Getter
private final List<GameItem> items; private final List<GameItem> items;
private boolean rendered;
private int listenerHashCode; private int listenerHashCode;
private int actualDisplayStartX; private int actualDisplayStartX;
@@ -167,7 +164,6 @@ public final class Chest extends GameObject implements UIClickHandler {
guiStart.getRow() - start.getY(), guiStart.getRow() - start.getY(),
guiEnd.getRow() - start.getY() guiEnd.getRow() - start.getY()
)); ));
rendered = true;
} }
private void clearPreviousUI( private void clearPreviousUI(
@@ -261,23 +257,15 @@ public final class Chest extends GameObject implements UIClickHandler {
if (!added) { if (!added) {
return true; return true;
} }
eventManager.emitEvent(new InventoryRerender());
eventManager.emitEvent(new SendSocketMessageEvent(new ItemTookFromChest(gameState.getCurrentRoom().getId(), item.getId())));
return true;
}
public void handleItemRemoval(GameItem item) {
items.remove(item); items.remove(item);
if (!rendered) {
return;
}
eventManager.emitEvent(new InventoryRerender());
if (items.isEmpty()) { if (items.isEmpty()) {
uiRoomClickHandlerRepository.removeHandlerForCurrentRoom(listenerHashCode); uiRoomClickHandlerRepository.removeHandlerForCurrentRoom(listenerHashCode);
} }
render(true); render(true);
return true;
} }
} }

View File

@@ -47,6 +47,5 @@ public class GameSetup {
); );
gameState.setCurrentRoom(rooms.getFirst()); gameState.setCurrentRoom(rooms.getFirst());
gameState.setAllRooms(rooms);
} }
} }

View File

@@ -1,11 +0,0 @@
package cz.jzitnik.client.game.setup.scenes;
import cz.jzitnik.client.screens.DeathScreen;
import cz.jzitnik.client.screens.Screen;
import cz.jzitnik.client.screens.scenes.Scene;
public class DeathScene extends Scene {
public DeathScene() {
super(new Screen[]{new DeathScreen()}, new OnEndAction.Repeat());
}
}

View File

@@ -1,11 +0,0 @@
package cz.jzitnik.client.game.setup.scenes;
import cz.jzitnik.client.screens.WinScreen;
import cz.jzitnik.client.screens.Screen;
import cz.jzitnik.client.screens.scenes.Scene;
public class WinScene extends Scene {
public WinScene() {
super(new Screen[]{new WinScreen()}, new OnEndAction.Repeat());
}
}

View File

@@ -1,46 +0,0 @@
package cz.jzitnik.client.screens;
import com.googlecode.lanterna.SGR;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.KeyboardPressEvent;
import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.states.TerminalState;
import java.io.IOException;
public class DeathScreen extends Screen {
@InjectState
private TerminalState terminalState;
@Override
public void fullRender() {
TerminalScreen screen = terminalState.getTerminalScreen();
screen.clear();
TextGraphics tg = terminalState.getTextGraphics();
int termWidth = screen.getTerminalSize().getColumns();
int termHeight = screen.getTerminalSize().getRows();
String message = "GAME OVER";
tg.setForegroundColor(TextColor.ANSI.RED);
tg.enableModifiers(SGR.BOLD);
tg.putString((termWidth - message.length()) / 2, termHeight / 2, message);
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void handleMouseAction(MouseAction event) {
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
}
}

View File

@@ -1,46 +0,0 @@
package cz.jzitnik.client.screens;
import com.googlecode.lanterna.SGR;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.KeyboardPressEvent;
import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.states.TerminalState;
import java.io.IOException;
public class WinScreen extends Screen {
@InjectState
private TerminalState terminalState;
@Override
public void fullRender() {
TerminalScreen screen = terminalState.getTerminalScreen();
screen.clear();
TextGraphics tg = terminalState.getTextGraphics();
int termWidth = screen.getTerminalSize().getColumns();
int termHeight = screen.getTerminalSize().getRows();
String message = "YOU WON!";
tg.setForegroundColor(TextColor.ANSI.GREEN);
tg.enableModifiers(SGR.BOLD);
tg.putString((termWidth - message.length()) / 2, termHeight / 2, message);
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void handleMouseAction(MouseAction event) {
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
}
}

View File

@@ -3,11 +3,10 @@ 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.TerminalResizeEvent; import cz.jzitnik.client.events.FullRoomDraw;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
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.creation.CreateGameResponse; import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -17,8 +16,6 @@ import lombok.extern.slf4j.Slf4j;
public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResponse> { public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResponse> {
@InjectState @InjectState
private GameState gameState; private GameState gameState;
@InjectState
private TerminalState terminalState;
@InjectDependency @InjectDependency
private EventManager eventManager; private EventManager eventManager;
@@ -27,6 +24,6 @@ public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResp
log.debug("Game code: {}", event.getGamePassword()); log.debug("Game code: {}", event.getGamePassword());
gameState.setPlayer(new Player(event.getOwnerPlayer())); gameState.setPlayer(new Player(event.getOwnerPlayer()));
gameState.setScreen(null); gameState.setScreen(null);
eventManager.emitEvent(new TerminalResizeEvent(terminalState.getTerminalScreen().getTerminalSize())); eventManager.emitEvent(new FullRoomDraw());
} }
} }

View File

@@ -1,34 +0,0 @@
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.game.GameState;
import cz.jzitnik.client.game.setup.scenes.WinScene;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.game.GameWin;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SocketEventHandler(GameWin.class)
public class GameWinHandler extends AbstractSocketEventHandler<GameWin> {
@InjectState
private GameState gameState;
@InjectDependency
private cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler roomTaskScheduler;
@Override
public void handle(GameWin event) {
log.debug("Game won!");
roomTaskScheduler.finalShutdown();
WinScene winScene = new WinScene();
gameState.setScreen(winScene);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
winScene.fullRender();
}
}

View File

@@ -1,43 +0,0 @@
package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.objects.Chest;
import cz.jzitnik.client.game.objects.GameObject;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
@SocketEventHandler(ItemTookFromChest.class)
public class ItemTookFromChestHandler extends AbstractSocketEventHandler<ItemTookFromChest> {
@InjectState
private GameState gameState;
@Override
public void handle(ItemTookFromChest event) {
for (GameRoom room : gameState.getAllRooms()) {
if (!room.getId().equals(event.roomId())) {
continue;
}
for (GameObject object : room.getObjects()) {
if (object instanceof Chest chest) {
var items = chest.getItems();
for (GameItem item : items) {
if (item.getId() != event.id()) {
continue;
}
items.remove(item);
chest.handleItemRemoval(item);
return;
}
}
}
}
}
}

View File

@@ -51,7 +51,6 @@ public class PlayerArrivalChangeHandler extends AbstractSocketEventHandler<Playe
OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.id()).findFirst().get(); OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.id()).findFirst().get();
otherPlayer.setVisible(event.arrived()); otherPlayer.setVisible(event.arrived());
otherPlayer.getPlayerCords().updateCords(event.playerCords()); otherPlayer.getPlayerCords().updateCords(event.playerCords());
otherPlayer.setPlayerRotation(event.playerRotation());
GameRoom currentRoom = gameState.getCurrentRoom(); GameRoom currentRoom = gameState.getCurrentRoom();
BufferedImage playerTexture = otherPlayer.getTexture(resourceManager); BufferedImage playerTexture = otherPlayer.getTexture(resourceManager);

View File

@@ -1,34 +0,0 @@
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.game.GameState;
import cz.jzitnik.client.game.setup.scenes.DeathScene;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.utils.DependencyManager;
import cz.jzitnik.common.socket.messages.game.PlayerDeath;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SocketEventHandler(PlayerDeath.class)
public class PlayerDeathHandler extends AbstractSocketEventHandler<PlayerDeath> {
@InjectState
private GameState gameState;
@InjectDependency
private DependencyManager dependencyManager;
@InjectDependency
private cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler roomTaskScheduler;
@Override
public void handle(PlayerDeath event) {
log.debug("Player death: {}", event.playerId());
roomTaskScheduler.finalShutdown();
DeathScene deathScene = new DeathScene();
dependencyManager.inject(deathScene);
gameState.setScreen(deathScene);
deathScene.fullRender();
}
}

View File

@@ -1,18 +0,0 @@
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.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.player.PlayerDisconnected;
@SocketEventHandler(PlayerDisconnected.class)
public class PlayerDisconnectedHandler extends AbstractSocketEventHandler<PlayerDisconnected> {
@InjectState
private GameState gameState;
@Override
public void handle(PlayerDisconnected event) {
gameState.getAllOtherPlayers().removeIf(player -> player.getId() == event.playerId());
}
}

View File

@@ -11,7 +11,6 @@ import cz.jzitnik.client.annotations.ui.UI;
import cz.jzitnik.client.events.MouseAction; import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.events.RerenderScreen; import cz.jzitnik.client.events.RerenderScreen;
import cz.jzitnik.client.events.handlers.DialogEventHandler; import cz.jzitnik.client.events.handlers.DialogEventHandler;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.dialog.OnEnd; import cz.jzitnik.client.game.dialog.OnEnd;
import cz.jzitnik.client.states.DialogState; import cz.jzitnik.client.states.DialogState;
import cz.jzitnik.client.states.ScreenBuffer; import cz.jzitnik.client.states.ScreenBuffer;
@@ -28,57 +27,42 @@ import static cz.jzitnik.client.events.handlers.DialogEventHandler.*;
@UI @UI
@Dependency @Dependency
public class DialogUI { public class DialogUI {
@InjectState @InjectState
private DialogState dialogState; private DialogState dialogState;
@InjectState @InjectState
private TerminalState terminalState; private TerminalState terminalState;
@InjectState
private ScreenBuffer screenBuffer;
@InjectState
private GameState gameState;
@InjectDependency @InjectDependency
private TextRenderer textRenderer; private TextRenderer textRenderer;
@InjectDependency @InjectDependency
private EventManager eventManager; private EventManager eventManager;
@InjectState
private ScreenBuffer screenBuffer;
@MouseHandler(MouseHandlerType.CLICK) @MouseHandler(MouseHandlerType.CLICK)
public boolean handleClick(MouseAction mouseAction) { public boolean handleClick(MouseAction mouseAction) {
if (dialogState.getCurrentDialog() == null || dialogState.isRenderInProgress()) { if (dialogState.getCurrentDialog() == null || dialogState.isRenderInProgress()) {
return false; return false;
} }
var dialog = dialogState.getCurrentDialog(); TerminalSize size = DialogEventHandler.getSize(textRenderer, dialogState.getCurrentDialog());
TerminalSize size = DialogEventHandler.getSize(textRenderer, dialog, gameState); TerminalPosition start = DialogEventHandler.getStart(terminalState.getTerminalScreen().getTerminalSize(), size);
TerminalPosition start = DialogEventHandler.getStart(
terminalState.getTerminalScreen().getTerminalSize(),
size
);
if (!(dialog.getOnEnd() instanceof OnEnd.AskQuestion askQuestion)) { if (!(dialogState.getCurrentDialog().getOnEnd() instanceof OnEnd.AskQuestion(
OnEnd.AskQuestion.Answer[] answers
))) {
setHoveredButtonIndex(-1); setHoveredButtonIndex(-1);
return false; return false;
} }
var answers = askQuestion.answers(gameState);
TerminalPosition mouse = mouseAction.getPosition(); TerminalPosition mouse = mouseAction.getPosition();
TerminalPosition mouseNormalized = new TerminalPosition( TerminalPosition mouseNormalized = new TerminalPosition(mouse.getColumn(), mouse.getRow() * 2);
mouse.getColumn(),
mouse.getRow() * 2
);
RerenderScreen.ScreenPart part = new RerenderScreen.ScreenPart( RerenderScreen.ScreenPart part = new RerenderScreen.ScreenPart(
start, start,
new TerminalPosition( new TerminalPosition(start.getColumn() + size.getColumns(), start.getRow() + size.getRows())
start.getColumn() + size.getColumns(),
start.getRow() + size.getRows()
)
); );
if (!part.isWithin(mouseNormalized)) { if (!part.isWithin(mouseNormalized)) {
@@ -86,13 +70,9 @@ public class DialogUI {
return false; return false;
} }
int buttonsStartY = DialogEventHandler.getYStartButtons(textRenderer, dialog); int buttonsStartY = DialogEventHandler.getYStartButtons(textRenderer, dialogState.getCurrentDialog());
TerminalPosition localPosition = new TerminalPosition( TerminalPosition localPosition = new TerminalPosition(mouseNormalized.getColumn() - start.getColumn(), mouseNormalized.getRow() - start.getRow() - buttonsStartY);
mouseNormalized.getColumn() - start.getColumn(), int buttonsHeight = DialogEventHandler.calculateButtonHeight(dialogState.getCurrentDialog());
mouseNormalized.getRow() - start.getRow() - buttonsStartY
);
int buttonsHeight = DialogEventHandler.calculateButtonHeight(dialog, gameState);
if (localPosition.getRow() < 0 || localPosition.getRow() >= buttonsHeight) { if (localPosition.getRow() < 0 || localPosition.getRow() >= buttonsHeight) {
setHoveredButtonIndex(-1); setHoveredButtonIndex(-1);
@@ -102,27 +82,23 @@ public class DialogUI {
int buttonIndex = localPosition.getRow() / (BUTTON_HEIGHT + BUTTON_PADDING); int buttonIndex = localPosition.getRow() / (BUTTON_HEIGHT + BUTTON_PADDING);
int rest = localPosition.getRow() % (BUTTON_HEIGHT + BUTTON_PADDING); int rest = localPosition.getRow() % (BUTTON_HEIGHT + BUTTON_PADDING);
if (buttonIndex < answers.length if (buttonIndex < answers.length && rest < BUTTON_HEIGHT && localPosition.getColumn() >= PADDING && localPosition.getColumn() < size.getColumns() - PADDING) {
&& rest < BUTTON_HEIGHT for (int y = start.getRow(); y < start.getRow() + size.getRows(); y++) {
&& localPosition.getColumn() >= PADDING for (int x = start.getColumn(); x < start.getColumn() + size.getColumns(); x++) {
&& localPosition.getColumn() < size.getColumns() - PADDING) { screenBuffer.getGlobalOverrideBuffer()[y][x] = new Empty();
}
clearDialog(start, size); }
eventManager.emitEvent( eventManager.emitEvent(
new Event[]{ new Event[]{
new RerenderScreen( new RerenderScreen(
new RerenderScreen.ScreenPart( new RerenderScreen.ScreenPart(
start, start,
new TerminalPosition( new TerminalPosition(start.getColumn() + size.getColumns(), start.getRow() + size.getRows())
start.getColumn() + size.getColumns(),
start.getRow() + size.getRows()
)
) )
), ),
answers[buttonIndex].dialog() answers[buttonIndex].dialog(),
} });
);
return true; return true;
} }
@@ -136,32 +112,21 @@ public class DialogUI {
return false; return false;
} }
var dialog = dialogState.getCurrentDialog(); TerminalSize size = DialogEventHandler.getSize(textRenderer, dialogState.getCurrentDialog());
TerminalSize size = DialogEventHandler.getSize(textRenderer, dialog, gameState); TerminalPosition start = DialogEventHandler.getStart(terminalState.getTerminalScreen().getTerminalSize(), size);
TerminalPosition start = DialogEventHandler.getStart(
terminalState.getTerminalScreen().getTerminalSize(),
size
);
if (!(dialog.getOnEnd() instanceof OnEnd.AskQuestion askQuestion)) { if (!(dialogState.getCurrentDialog().getOnEnd() instanceof OnEnd.AskQuestion(
OnEnd.AskQuestion.Answer[] answers
))) {
setHoveredButtonIndex(-1); setHoveredButtonIndex(-1);
return false; return false;
} }
var answers = askQuestion.answers(gameState);
TerminalPosition mouse = mouseAction.getPosition(); TerminalPosition mouse = mouseAction.getPosition();
TerminalPosition mouseNormalized = new TerminalPosition( TerminalPosition mouseNormalized = new TerminalPosition(mouse.getColumn(), mouse.getRow() * 2);
mouse.getColumn(),
mouse.getRow() * 2
);
RerenderScreen.ScreenPart part = new RerenderScreen.ScreenPart( RerenderScreen.ScreenPart part = new RerenderScreen.ScreenPart(
start, start,
new TerminalPosition( new TerminalPosition(start.getColumn() + size.getColumns(), start.getRow() + size.getRows())
start.getColumn() + size.getColumns(),
start.getRow() + size.getRows()
)
); );
if (!part.isWithin(mouseNormalized)) { if (!part.isWithin(mouseNormalized)) {
@@ -169,13 +134,9 @@ public class DialogUI {
return false; return false;
} }
int buttonsStartY = DialogEventHandler.getYStartButtons(textRenderer, dialog); int buttonsStartY = DialogEventHandler.getYStartButtons(textRenderer, dialogState.getCurrentDialog());
TerminalPosition localPosition = new TerminalPosition( TerminalPosition localPosition = new TerminalPosition(mouseNormalized.getColumn() - start.getColumn(), mouseNormalized.getRow() - start.getRow() - buttonsStartY);
mouseNormalized.getColumn() - start.getColumn(), int buttonsHeight = DialogEventHandler.calculateButtonHeight(dialogState.getCurrentDialog());
mouseNormalized.getRow() - start.getRow() - buttonsStartY
);
int buttonsHeight = DialogEventHandler.calculateButtonHeight(dialog, gameState);
if (localPosition.getRow() < 0 || localPosition.getRow() >= buttonsHeight) { if (localPosition.getRow() < 0 || localPosition.getRow() >= buttonsHeight) {
setHoveredButtonIndex(-1); setHoveredButtonIndex(-1);
@@ -185,11 +146,7 @@ public class DialogUI {
int buttonIndex = localPosition.getRow() / (BUTTON_HEIGHT + BUTTON_PADDING); int buttonIndex = localPosition.getRow() / (BUTTON_HEIGHT + BUTTON_PADDING);
int rest = localPosition.getRow() % (BUTTON_HEIGHT + BUTTON_PADDING); int rest = localPosition.getRow() % (BUTTON_HEIGHT + BUTTON_PADDING);
if (buttonIndex < answers.length if (buttonIndex < answers.length && rest < BUTTON_HEIGHT && localPosition.getColumn() >= PADDING && localPosition.getColumn() < size.getColumns() - PADDING) {
&& rest < BUTTON_HEIGHT
&& localPosition.getColumn() >= PADDING
&& localPosition.getColumn() < size.getColumns() - PADDING) {
setHoveredButtonIndex(buttonIndex); setHoveredButtonIndex(buttonIndex);
return true; return true;
} }
@@ -204,12 +161,4 @@ public class DialogUI {
eventManager.emitEvent(dialogState.getCurrentDialog()); eventManager.emitEvent(dialogState.getCurrentDialog());
} }
} }
private void clearDialog(TerminalPosition start, TerminalSize size) {
for (int y = start.getRow(); y < start.getRow() + size.getRows(); y++) {
for (int x = start.getColumn(); x < start.getColumn() + size.getColumns(); x++) {
screenBuffer.getGlobalOverrideBuffer()[y][x] = new Empty();
}
}
}
} }

View File

@@ -18,7 +18,6 @@ import tools.jackson.databind.*;
import tools.jackson.dataformat.yaml.YAMLFactory; import tools.jackson.dataformat.yaml.YAMLFactory;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -49,7 +48,7 @@ public class DependencyManager extends InjectableValues {
// Construct all classes // Construct all classes
for (Class<?> clazz : classes) { for (Class<?> clazz : classes) {
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { for (var constructor : clazz.getDeclaredConstructors()) {
var paramTypes = constructor.getParameterTypes(); var paramTypes = constructor.getParameterTypes();
var params = new Object[paramTypes.length]; var params = new Object[paramTypes.length];
boolean suitable = true; boolean suitable = true;

View File

@@ -64,6 +64,7 @@ public class EventManager extends Thread {
} }
} }
@SuppressWarnings("unchecked")
public EventManager(Reflections reflections, DependencyManager dependencyManager) { public EventManager(Reflections reflections, DependencyManager dependencyManager) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
setDaemon(true); setDaemon(true);

View File

@@ -1,3 +1,3 @@
renderColliders: true renderColliders: false
renderPlayerCollider: true renderPlayerCollider: false
showPlayerCordsLogs: true showPlayerCordsLogs: false

View File

@@ -1,331 +1,208 @@
# =========================
# START ROOM
# Intro NPC explains the goal + starter chest
# =========================
- id: "spawn" - id: "spawn"
texture: "ROOM11" texture: "ROOM1"
colliders: #mobs:
- start: { x: 76, y: 45 } # - type: "hittable_drops"
end: { x: 155, y: 67 } # texture: "PLAYER_FRONT"
- start: { x: 40, y: 160 } # cords:
end: { x: 70, y: 190 } # x: 100
- start: { x: 150, y: 160 } # y: 100
end: { x: 185, y: 190 } # collider:
# start:
mobs: # x: 0
- type: "dialog" # y: 52
texture: "OLD_MAN" # end:
cords: { x: 90, y: 90 } # x: 44
collider: # y: 78
start: { x: 0, y: 52 } # health: 10
end: { x: 44, y: 78 } # itemsDrops:
dialog: # - name: "Apple"
text: "You fell down here too? This cave is cursed..." # type:
onEnd: # name: "food"
type: continue # addHealth: 1
nextDialog: # texture: "APPLE"
text: "The only way out is guarded by a beast deep inside." # tasks:
onEnd: # - type: "blind_following_player"
type: continue # speed: 1
nextDialog: # updateRateMs: 100
text: "Kill it. Bring its skin to the Key Keeper." # - type: "attacking_player"
onEnd: # damage: 5
type: continue # reach: 15
nextDialog: # updateRateMs: 500
text: "He will give you the key to the final gate." west: "empty"
onEnd: { type: end }
west: "empty_a"
east: "truhlaright" east: "truhlaright"
north: null
south: null south: null
# =========================
# LOOT ROOM 1
# =========================
- id: "truhlaright" - id: "truhlaright"
texture: "ROOM5" texture: "ROOM1"
colliders: objects:
- start: { x: 90, y: 100 } - objectType: "chest"
end: { x: 140, y: 145 } cords:
- start: { x: 40, y: 70 } x: 100
end: { x: 80, y: 100 } y: 45
west: "spawn" items:
east: "filler_1" - name: "Wooden sword"
north: "klicnik" type:
south: "filler_south_1" name: "weapon_sword"
dealDamage: 1
# ========================= texture: "WOODEN_SWORD"
# KEY KEEPER (QUEST NPC) - name: "Apple"
# ========================= type:
- id: "klicnik" name: "food"
texture: "ROOM10" addHealth: 1
mobs:
- type: "dialog"
texture: "KEY_KEEPER"
cords: { x: 90, y: 60 }
collider:
start: { x: 0, y: 52 }
end: { x: 44, y: 78 }
dialog:
text: "Want to leave? Bring me the beast's skin."
onEnd:
type: ask_question
answers:
- answer: "I have it"
requirement:
item: "BeastSkin"
dialog:
text: "Well done. Here is the key."
onEnd:
type: "give_item"
item:
id: 800
name: "Key"
type: { name: "key" }
texture: "APPLE" texture: "APPLE"
then: { type: end } colliders:
- answer: "Not yet" - start:
dialog: x: 100
text: "Then go back before it finds you." y: 45
onEnd: { type: end } end:
x: 140
west: "filler_k_west" y: 67
east: "truhlarightright" west: "spawn"
north: "final_room"
south: "truhlaright"
# =========================
# BOSS ROOM
# =========================
- id: "boss"
texture: "ROOM3"
mobs:
- type: "hittable_drops"
texture: "CAVE_BEAST"
cords: { x: 100, y: 100 }
collider:
start: { x: 0, y: 52 }
end: { x: 44, y: 78 }
health: 40
itemsDrops:
- id: 200
name: "Beast Skin"
type: { name: "beast_skin" }
texture: "BOSS_SKIN"
tasks:
- type: "following_player"
speed: 2
updateRateMs: 80
- type: "attacking_player"
damage: 8
reach: 18
updateRateMs: 400
west: "filler_boss_west"
east: null east: null
north: null north: "klicnik"
south: "empty_c" south: null
- id: "empty"
texture: "ROOM1"
# =========================
# FINAL ROOM / EXIT
# =========================
- id: "final_room"
texture: "ROOM6"
requirement:
item: "Key"
end: true
west: null west: null
east: "spawn"
north: "truhlatop"
south: null
- id: "truhlatop"
texture: "ROOM1"
objects:
- objectType: "chest"
cords:
x: 100
y: 45
items:
- name: "Wooden sword"
type:
name: "weapon_sword"
dealDamage: 1
texture: "WOODEN_SWORD"
- name: "Apple"
type:
name: "food"
addHealth: 1
texture: "APPLE"
colliders:
- start:
x: 100
y: 45
end:
x: 140
y: 67
west: "empty2"
east: null east: null
north: null north: null
south: "klicnik" south: "empty"
- id: "empty2"
texture: "ROOM1"
west: null
east: "truhlatop"
north: "boss"
south: null
- id: "boss"
# =========================
# COMBAT FILLER A (zombie)
# =========================
- id: "empty_a"
texture: "ROOM7"
mobs:
- type: "hittable_drops"
texture: "ZOMBIE"
cords: { x: 110, y: 100 }
collider:
start: { x: 0, y: 52 }
end: { x: 44, y: 78 }
health: 6
tasks:
- type: "following_player"
speed: 1
updateRateMs: 120
- type: "attacking_player"
damage: 2
reach: 15
updateRateMs: 700
west: "filler_2"
east: "spawn"
north: "empty_c"
south: "filler_deadend_1"
# =========================
# COMBAT FILLER B (blind hunter)
# =========================
- id: "empty_c"
texture: "ROOM1" texture: "ROOM1"
mobs: mobs:
- type: "hittable_drops" - type: "hittable_drops"
texture: "BLIND_HUNTER" texture: "PLAYER_FRONT"
cords: { x: 100, y: 100 } cords:
x: 100
y: 100
collider: collider:
start: { x: 0, y: 52 } start:
end: { x: 44, y: 78 } x: 0
health: 20 y: 52
end:
x: 44
y: 78
health: 10
itemsDrops:
- name: "Apple"
type:
name: "food"
addHealth: 1
texture: "APPLE"
tasks:
- type: "following_player"
speed: 1
updateRateMs: 100
- type: "attacking_player"
damage: 20
reach: 15
updateRateMs: 500
west: null
east: null
north: null
south: "empty2"
- id: "klicnik"
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: tasks:
- type: "blind_following_player" - type: "blind_following_player"
speed: 1 speed: 1
updateRateMs: 100 updateRateMs: 100
- type: "attacking_player" - type: "attacking_player"
damage: 6 damage: 5
reach: 15 reach: 15
updateRateMs: 500 updateRateMs: 500
west: "filler_c_west" west: null
east: null east: "truhlarightright"
north: "boss" north: null
south: "empty_a" south: "truhlaright"
# =========================
# LOOT ROOM 2
# =========================
- id: "truhlarightright" - id: "truhlarightright"
texture: "ROOM2" texture: "ROOM1"
west: "klicnik"
east: null
north: null
south: null
objects: objects:
- objectType: "chest" - objectType: "chest"
cords: { x: 100, y: 45 } cords:
x: 100
y: 45
items: items:
- id: 7 - name: "Wooden sword"
name: "Axe" type:
type: { name: "weapon_sword", dealDamage: 4, attackCooldownMs: 800 } # TODO: Make it an axe name: "weapon_sword"
texture: "AXE" dealDamage: 1
- id: 8 texture: "WOODEN_SWORD"
name: "Apple" - name: "Apple"
type: { name: "food", addHealth: 2 } type:
name: "food"
addHealth: 1
texture: "APPLE" texture: "APPLE"
colliders: colliders:
- start: { x: 100, y: 45 } - start:
end: { x: 140, y: 67 } x: 100
west: "klicnik" y: 45
east: "filler_rr_east" end:
north: null x: 140
south: null y: 67
# =========================
# EXTRA FILLER ROOMS (maze / dead ends)
# =========================
- id: "filler_1"
texture: "ROOM3"
west: "truhlaright"
east: "filler_1b"
north: null
south: null
- id: "filler_1b"
texture: "ROOM1"
west: "filler_1"
east: null
north: null
south: null
- id: "filler_2"
texture: "ROOM7"
west: null
east: "empty_a"
north: "filler_loop_1"
south: null
- id: "filler_loop_1"
texture: "ROOM4"
west: null
east: null
north: "filler_loop_2"
south: "filler_2"
- id: "filler_loop_2"
texture: "ROOM3"
west: "filler_loop_3"
east: null
north: null
south: "filler_loop_1"
- id: "filler_loop_3"
texture: "ROOM2"
west: null
east: "filler_loop_2"
north: null
south: null
- id: "filler_deadend_1"
texture: "ROOM1"
west: null
east: null
north: "empty_a"
south: null
- id: "filler_k_west"
texture: "ROOM4"
west: "filler_k_west_2"
east: "klicnik"
north: null
south: null
- id: "filler_k_west_2"
texture: "ROOM3"
west: null
east: "filler_k_west"
north: null
south: null
- id: "filler_boss_west"
texture: "ROOM2"
west: null
east: "boss"
north: null
south: null
- id: "filler_c_west"
texture: "ROOM1"
west: null
east: "empty_c"
north: null
south: null
- id: "filler_rr_east"
texture: "ROOM4"
west: "truhlarightright"
east: null
north: null
south: null
- id: "filler_south_1"
texture: "ROOM3"
west: null
east: null
north: "truhlaright"
south: "filler_south_2"
- id: "filler_south_2"
texture: "ROOM2"
west: null
east: null
north: "filler_south_1"
south: null

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -5,16 +5,17 @@ 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.PlayerArrivalChange;
import cz.jzitnik.common.socket.messages.player.PlayerJoined; import cz.jzitnik.common.socket.messages.player.PlayerJoined;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
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;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.game.Player; import cz.jzitnik.server.game.Player;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader; import tools.jackson.databind.ObjectReader;
import tools.jackson.dataformat.yaml.YAMLFactory; import tools.jackson.dataformat.yaml.YAMLFactory;
@Slf4j
@EventHandler(ConnectToAGame.class) @EventHandler(ConnectToAGame.class)
public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame> { public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame> {
public ConnectToAGameHandler(GlobalContext globalContext) { public ConnectToAGameHandler(GlobalContext globalContext) {
@@ -23,6 +24,7 @@ public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame>
@Override @Override
public void handle(ConnectToAGame event, Client client) { public void handle(ConnectToAGame event, Client client) {
log.debug("Pepa");
var gameOptional = globalContext.getGame(event.gamePass().toUpperCase()); var gameOptional = globalContext.getGame(event.gamePass().toUpperCase());
if (gameOptional.isEmpty()) { if (gameOptional.isEmpty()) {
@@ -47,8 +49,8 @@ public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame>
cl.getSession().sendMessage(new PlayerJoined(player)); cl.getSession().sendMessage(new PlayerJoined(player));
if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) { if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) {
cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), player.getPlayerCords(), PlayerRotation.FRONT, true, true)); cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), player.getPlayerCords(), true, true));
client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), cl.getPlayer().getCords(), PlayerRotation.FRONT, true, false)); client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), cl.getPlayer().getCords(), true, false));
} }
} }

View File

@@ -1,21 +0,0 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.game.GameWin;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
@EventHandler(GameWin.class)
public class GameWinHandler extends AbstractEventHandler<GameWin> {
public GameWinHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(GameWin event, Client client) {
for (Client player : client.getGame().getPlayers()) {
player.getSession().sendMessage(new GameWin());
}
}
}

View File

@@ -1,23 +0,0 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
@EventHandler(ItemTookFromChest.class)
public class ItemTookFromChestHandler extends AbstractEventHandler<ItemTookFromChest> {
public ItemTookFromChestHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(ItemTookFromChest event, Client client) {
client.getGame().getItemModifiers().add(event);
for (Client client1 : client.getGame().getPlayers()) {
client1.getSession().sendMessage(event);
}
}
}

View File

@@ -40,9 +40,9 @@ public class MovePlayerRoomHandler extends AbstractEventHandler<MovePlayerRoom>
if (player.getPlayer().getId() != client.getPlayer().getId()) { if (player.getPlayer().getId() != client.getPlayer().getId()) {
if (player.getPlayer().getCurrentRoom().equals(oldRoomId)) { if (player.getPlayer().getCurrentRoom().equals(oldRoomId)) {
log.debug("{}", event.oldCords()); log.debug("{}", event.oldCords());
player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.oldCords(), client.getPlayer().getPlayerRotation(), false, true)); player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.oldCords(), false, true));
} else if (player.getPlayer().getCurrentRoom().equals(event.newRoomId())) { } else if (player.getPlayer().getCurrentRoom().equals(event.newRoomId())) {
player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.newCords(), client.getPlayer().getPlayerRotation(), true, true)); player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.newCords(), true, true));
} }
} }
} }

View File

@@ -1,21 +0,0 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.game.PlayerDeath;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
@EventHandler(PlayerDeath.class)
public class PlayerDeathHandler extends AbstractEventHandler<PlayerDeath> {
public PlayerDeathHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(PlayerDeath event, Client client) {
for (Client player : client.getGame().getPlayers()) {
player.getSession().sendMessage(new PlayerDeath(event.playerId()));
}
}
}

View File

@@ -1,10 +1,8 @@
package cz.jzitnik.server.game; package cz.jzitnik.server.game;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Getter @Getter
@@ -12,5 +10,4 @@ import java.util.List;
public class Game { public class Game {
private String password; private String password;
private List<Client> players; private List<Client> players;
private final List<ItemTookFromChest> itemModifiers = new ArrayList<>();
} }

View File

@@ -1,8 +1,6 @@
package cz.jzitnik.server.socket; package cz.jzitnik.server.socket;
import cz.jzitnik.common.socket.SocketMessage; import cz.jzitnik.common.socket.SocketMessage;
import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange;
import cz.jzitnik.common.socket.messages.player.PlayerDisconnected;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.context.GlobalContext; import cz.jzitnik.server.context.GlobalContext;
import jakarta.websocket.*; import jakarta.websocket.*;
@@ -39,24 +37,7 @@ public class WebSocket {
@OnClose @OnClose
public void onClose(Session session, CloseReason reason) { public void onClose(Session session, CloseReason reason) {
Client client = globalContext.getSessions().get(session); System.out.println("Connection closed: " + reason);
client.getGame().getPlayers().remove(client);
for (Client otherClient : client.getGame().getPlayers()) {
if (otherClient.getPlayer().getCurrentRoom().equals(client.getPlayer().getCurrentRoom())) {
otherClient.getSession().sendMessage(new PlayerArrivalChange(
client.getPlayer().getId(),
client.getPlayer().getCords(),
client.getPlayer().getPlayerRotation(),
false,
true
));
}
otherClient.getSession().sendMessage(new PlayerDisconnected(client.getPlayer().getId()));
}
log.debug("Connection closed: {}", reason);
} }
@OnError @OnError