feat: Implemented interactions and rooms
Now you can interact with characters and go between rooms
This commit is contained in:
@ -46,7 +46,7 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Cli.info("Prosím počkejte...");
|
||||
chronos.waitForStart();
|
||||
Cli.info("Hra začala!");
|
||||
System.out.println("\n\n");
|
||||
|
@ -1,11 +1,10 @@
|
||||
package cz.jzitnik.api;
|
||||
|
||||
import cz.jzitnik.api.requests.InteractionRequest;
|
||||
import cz.jzitnik.api.requests.PlayerNameRequest;
|
||||
import cz.jzitnik.api.responses.InteractionResponse;
|
||||
import cz.jzitnik.api.responses.UnifiedResponse;
|
||||
import cz.jzitnik.api.types.Game;
|
||||
import cz.jzitnik.api.types.Room;
|
||||
import cz.jzitnik.api.types.TakeItemsResponse;
|
||||
import cz.jzitnik.api.types.TestGameKeyResponse;
|
||||
import cz.jzitnik.api.types.*;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.GET;
|
||||
@ -27,6 +26,17 @@ public interface ApiService {
|
||||
@Body PlayerNameRequest requestBody
|
||||
);
|
||||
|
||||
@GET("game/players/me")
|
||||
Call<UnifiedResponse<Player, Error>> getMe(
|
||||
@Query("playerKey") String playerKey
|
||||
);
|
||||
|
||||
@POST("game/players/use_item")
|
||||
Call<UnifiedResponse<Object, Error>> useItem(
|
||||
@Query("playerKey") String playerKey,
|
||||
@Query("itemId") Long itemId
|
||||
);
|
||||
|
||||
@POST("game/start")
|
||||
Call<UnifiedResponse<Object, Error>> startGame(
|
||||
@Query("playerKey") String playerKey,
|
||||
@ -59,4 +69,16 @@ public interface ApiService {
|
||||
@Query("playerKey") String playerKey,
|
||||
@Query("characterId") Long characterId
|
||||
);
|
||||
|
||||
@GET("game/characters/interaction")
|
||||
Call<UnifiedResponse<InteractionData, Error>> getInteractionData(
|
||||
@Query("playerKey") String playerKey,
|
||||
@Query("characterId") Long characterId
|
||||
);
|
||||
|
||||
@POST("game/characters/interact")
|
||||
Call<UnifiedResponse<InteractionResponse, Error>> interact(
|
||||
@Query("playerKey") String playerKey,
|
||||
@Body InteractionRequest interactionRequest
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package cz.jzitnik.api.requests;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class InteractionRequest {
|
||||
private String data;
|
||||
private Long characterId;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cz.jzitnik.api.responses;
|
||||
|
||||
import cz.jzitnik.api.types.Item;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class InteractionResponse {
|
||||
private boolean success;
|
||||
private String responseText;
|
||||
private List<Item> items;
|
||||
}
|
@ -9,4 +9,9 @@ public class Item {
|
||||
private Long id;
|
||||
|
||||
private ItemType itemType;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return itemType.toString();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,27 @@
|
||||
package cz.jzitnik.api.types;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum ItemType {
|
||||
KEY_FRAGMENT,
|
||||
LUCK_POTION
|
||||
LUCK_POTION;
|
||||
|
||||
private static final Set<ItemType> usableItems = new HashSet<>();
|
||||
|
||||
static {
|
||||
usableItems.add(ItemType.LUCK_POTION);
|
||||
}
|
||||
|
||||
public boolean isUsable() {
|
||||
return usableItems.contains(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return switch (this) {
|
||||
case LUCK_POTION -> "Lektvar štěstí";
|
||||
case KEY_FRAGMENT -> "Fragment klíče";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cz.jzitnik.api.types;
|
||||
|
||||
import cz.jzitnik.utils.Cli;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@ -15,4 +16,30 @@ public class Player {
|
||||
private List<Item> inventory;
|
||||
|
||||
private Room currentRoom;
|
||||
|
||||
private boolean luck;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String sb = "Hráč: " +
|
||||
Cli.Colors.BLUE +
|
||||
name +
|
||||
Cli.Colors.RESET +
|
||||
"\n\n" +
|
||||
"Vlastnosti:\n" +
|
||||
"Id: " +
|
||||
id +
|
||||
"\n" +
|
||||
"Štěstí: " +
|
||||
(luck ? Cli.Colors.GREEN : Cli.Colors.RED) +
|
||||
(luck ? "Ano" : "Ne") +
|
||||
Cli.Colors.RESET +
|
||||
"\n" +
|
||||
"Místnost: " +
|
||||
Cli.Colors.BLUE +
|
||||
currentRoom +
|
||||
Cli.Colors.RESET;
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
@ -15,4 +15,13 @@ public class Room {
|
||||
private List<Item> items;
|
||||
|
||||
private List<Character> characters;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (name.equals("Outside")) {
|
||||
return "Venek";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,8 @@ package cz.jzitnik.game;
|
||||
|
||||
import cz.jzitnik.api.ApiService;
|
||||
import cz.jzitnik.api.requests.PlayerNameRequest;
|
||||
import cz.jzitnik.api.types.*;
|
||||
import cz.jzitnik.api.types.Character;
|
||||
import cz.jzitnik.api.types.Room;
|
||||
import cz.jzitnik.api.types.TakeItemsResponse;
|
||||
import cz.jzitnik.api.types.TestGameKeyResponse;
|
||||
import cz.jzitnik.game.interactions.Interactions;
|
||||
import cz.jzitnik.utils.Cli;
|
||||
import cz.jzitnik.utils.ConfigPathProvider;
|
||||
@ -220,15 +218,11 @@ public class Chronos {
|
||||
Cli.info("Nyní se nacházíte v místnosti " + room.getName());
|
||||
}
|
||||
|
||||
if (room.getCharacters().isEmpty()) {
|
||||
var characters = room.getCharacters();
|
||||
if (characters.isEmpty()) {
|
||||
Cli.type("V místnosti je prázno... Ani jedna duše.");
|
||||
}
|
||||
for (Character character : room.getCharacters()) {
|
||||
talkWith(character);
|
||||
}
|
||||
|
||||
if (!room.getCharacters().isEmpty()) {
|
||||
interactWithAny(room.getCharacters());
|
||||
} else {
|
||||
talk(characters);
|
||||
}
|
||||
|
||||
var responseRooms = apiService.getAllRooms(localData.getUserSecret()).execute();
|
||||
@ -237,46 +231,26 @@ public class Chronos {
|
||||
System.out.println();
|
||||
Cli.info("Nyni si vyberte do jaké místnosti chcete jít.");
|
||||
|
||||
int selectedIndex = Cli.selectOptionIndex(rooms.stream().map(Room::getName).toList());
|
||||
int selectedIndex = Cli.selectOptionIndex(rooms.stream().map(Room::toString).toList());
|
||||
|
||||
visit(rooms.get(selectedIndex).getId(), true);
|
||||
}
|
||||
|
||||
private void interactWithAny(List<Character> characters) throws IOException {
|
||||
if (characters.stream().anyMatch(character -> character.getInteraction() != null)) {
|
||||
List<Character> interactablePostavy = characters.stream().filter(
|
||||
character -> character.getInteraction() != null &&
|
||||
!character.isInteractedWith()
|
||||
).toList();
|
||||
public void talk(List<Character> characters) throws IOException {
|
||||
List<String> commands = new ArrayList<>(characters.stream().map(chachar -> "Promluvit si s " + chachar.getName()).toList());
|
||||
commands.add("Přejít do jiné místnosti");
|
||||
CommandPalette commandPalette = new CommandPalette(commands, apiService, localData.getUserSecret());
|
||||
int selectedIndex = commandPalette.displayIndex();
|
||||
|
||||
if (interactablePostavy.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> options = new ArrayList<>(interactablePostavy.stream().map(Character::getName).toList());
|
||||
options.add("Neinteragovat s nikým");
|
||||
|
||||
Cli.info("Můžete interagovat s následujícími postavami:");
|
||||
int selectedIndex = Cli.selectOptionIndex(options);
|
||||
|
||||
if (selectedIndex == options.size() - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var character = characters.get(selectedIndex);
|
||||
|
||||
Interactions.runInteraction(character);
|
||||
|
||||
var currentRoom = apiService.getCurrentRoom(localData.getUserSecret()).execute().body().getData();
|
||||
|
||||
interactWithAny(currentRoom.get().getCharacters().stream().filter(chachar -> !chachar.isInteractedWith()).toList());
|
||||
if (selectedIndex == commands.size() - 1) {
|
||||
// Přejít do jiné místnosti
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void talkWith(Character character) throws IOException {
|
||||
Cli.type("V místnosti je " + character.getName());
|
||||
// Dangerous I would say but whatever
|
||||
Character character = characters.get(selectedIndex);
|
||||
System.out.println();
|
||||
Cli.type(Cli.Colors.YELLOW + character.getName() + ": " + Cli.Colors.RESET + character.getDialog());
|
||||
Cli.type(character, character.getDialog());
|
||||
|
||||
if (!character.getInventory().isEmpty()) {
|
||||
var res = apiService.takeItems(getLocalData().getUserSecret(), character.getId()).execute();
|
||||
@ -285,9 +259,19 @@ public class Chronos {
|
||||
if (body == TakeItemsResponse.ALREADY_TAKEN) {
|
||||
Cli.type(Cli.Colors.YELLOW + character.getName() + ": " + Cli.Colors.RESET + "Už jsem ti mé itemy dal. Už pro tebe nic nemám.");
|
||||
} else {
|
||||
var characters = character.getInventory().stream().map(item -> item.getItemType().toString()).toList();
|
||||
Cli.info("Dostal jste: " + String.join(", ", characters));
|
||||
Cli.gotItems(character.getInventory());
|
||||
}
|
||||
}
|
||||
|
||||
// Interact with the character if it is interactable
|
||||
if (character.getInteraction() != null) {
|
||||
Interactions.runInteraction(character, apiService, localData.getUserSecret());
|
||||
}
|
||||
|
||||
// Refetch the characters
|
||||
var res = apiService.getCurrentRoom(localData.getUserSecret()).execute();
|
||||
var room = res.body().getData().get();
|
||||
|
||||
talk(room.getCharacters());
|
||||
}
|
||||
}
|
99
frontend/src/main/java/cz/jzitnik/game/CommandPalette.java
Normal file
99
frontend/src/main/java/cz/jzitnik/game/CommandPalette.java
Normal file
@ -0,0 +1,99 @@
|
||||
package cz.jzitnik.game;
|
||||
|
||||
import cz.jzitnik.api.ApiService;
|
||||
import cz.jzitnik.utils.Cli;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class CommandPalette {
|
||||
public enum CommandType {
|
||||
CUSTOM,
|
||||
INVENTORY_OPEN,
|
||||
USER_PROFILE,
|
||||
EXIT,
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public static class Command {
|
||||
private String name;
|
||||
private CommandType commandType;
|
||||
|
||||
public Command(String name) {
|
||||
this.name = name;
|
||||
this.commandType = CommandType.CUSTOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (commandType == CommandType.CUSTOM) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return Cli.Colors.BLUE + name + Cli.Colors.RESET;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Command> commands;
|
||||
private ApiService apiService;
|
||||
private String playerKey;
|
||||
|
||||
public CommandPalette(List<String> commands, ApiService apiService, String playerKey) {
|
||||
this.commands = commands.stream().map(Command::new).toList();
|
||||
this.apiService = apiService;
|
||||
this.playerKey = playerKey;
|
||||
}
|
||||
|
||||
public int displayIndex() throws IOException {
|
||||
List<Command> allCommands = new ArrayList<>(commands);
|
||||
|
||||
// Command that can be used anywhere
|
||||
allCommands.add(new Command("Otevřít inventář", CommandType.INVENTORY_OPEN));
|
||||
allCommands.add(new Command("Otevřít profil uživatele", CommandType.USER_PROFILE));
|
||||
allCommands.add(new Command("Odejít ze hry", CommandType.EXIT));
|
||||
|
||||
System.out.println("\n");
|
||||
var selectedOption = Cli.selectOptionIndex(allCommands.stream().map(Command::toString).toList());
|
||||
|
||||
var command = allCommands.get(selectedOption);
|
||||
|
||||
return switch (command.getCommandType()) {
|
||||
case CUSTOM -> selectedOption;
|
||||
case EXIT -> {
|
||||
System.out.println("Exiting game...");
|
||||
System.exit(0);
|
||||
|
||||
yield 0;
|
||||
}
|
||||
case INVENTORY_OPEN -> {
|
||||
var response = apiService.getMe(playerKey).execute();
|
||||
assert response.body() != null;
|
||||
var body = response.body().getData();
|
||||
var me = body.get();
|
||||
|
||||
var inventory = new Inventory(me, apiService, playerKey);
|
||||
inventory.display();
|
||||
|
||||
yield displayIndex();
|
||||
}
|
||||
case USER_PROFILE -> {
|
||||
var response = apiService.getMe(playerKey).execute();
|
||||
assert response.body() != null;
|
||||
var body = response.body().getData();
|
||||
var me = body.get();
|
||||
|
||||
System.out.println("\n\n" + me);
|
||||
|
||||
yield displayIndex();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
69
frontend/src/main/java/cz/jzitnik/game/Inventory.java
Normal file
69
frontend/src/main/java/cz/jzitnik/game/Inventory.java
Normal file
@ -0,0 +1,69 @@
|
||||
package cz.jzitnik.game;
|
||||
|
||||
import cz.jzitnik.api.ApiService;
|
||||
import cz.jzitnik.api.types.Item;
|
||||
import cz.jzitnik.api.types.ItemType;
|
||||
import cz.jzitnik.api.types.Player;
|
||||
import cz.jzitnik.utils.Cli;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Inventory {
|
||||
private Player player;
|
||||
private ApiService apiService;
|
||||
private String playerKey;
|
||||
|
||||
|
||||
public void display() throws IOException {
|
||||
System.out.println();
|
||||
Cli.info("Váš inventář:");
|
||||
|
||||
// Create a map to group items by ItemType and count occurrences
|
||||
Map<ItemType, Integer> itemCounts = new HashMap<>();
|
||||
List<Item> usableItemList = new ArrayList<>();
|
||||
|
||||
for (Item item : player.getInventory()) {
|
||||
ItemType itemType = item.getItemType();
|
||||
|
||||
// If the item is usable, add it to the usableItemList
|
||||
if (itemType.isUsable()) {
|
||||
usableItemList.add(item);
|
||||
} else {
|
||||
// Otherwise, count it in the itemCounts map
|
||||
itemCounts.put(itemType, itemCounts.getOrDefault(itemType, 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Display grouped non-usable items
|
||||
for (Map.Entry<ItemType, Integer> entry : itemCounts.entrySet()) {
|
||||
System.out.println(entry.getValue() + "x " + entry.getKey());
|
||||
}
|
||||
|
||||
// Display usable items at the end
|
||||
for (Item usableItem : usableItemList) {
|
||||
System.out.println("1x " + Cli.Colors.BLUE + usableItem + Cli.Colors.RESET + " (lze využít)");
|
||||
}
|
||||
|
||||
|
||||
if (!usableItemList.isEmpty()) {
|
||||
System.out.println("\n\nNyní si můžete vybrat jaký item chcete využít");
|
||||
var options = new ArrayList<>(usableItemList.stream().map(Item::toString).toList());
|
||||
options.add("Zavřít inventář");
|
||||
|
||||
var selectedIndex = Cli.selectOptionIndex(options);
|
||||
|
||||
if (selectedIndex == options.size() - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var item = usableItemList.get(selectedIndex);
|
||||
|
||||
apiService.useItem(playerKey, item.getId()).execute();
|
||||
|
||||
Cli.info(item + " byl využit!");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,40 @@
|
||||
package cz.jzitnik.game.interactions;
|
||||
|
||||
import cz.jzitnik.api.ApiService;
|
||||
import cz.jzitnik.api.requests.InteractionRequest;
|
||||
import cz.jzitnik.api.types.Character;
|
||||
import cz.jzitnik.utils.Cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Interactions {
|
||||
public static void runInteraction(Character character) {
|
||||
public static void runInteraction(Character character, ApiService apiService, String playerKey) throws IOException {
|
||||
var response = apiService.getInteractionData(playerKey, character.getId()).execute();
|
||||
var interactionData = response.body().getData().get();
|
||||
|
||||
if (character.isInteractedWith()) {
|
||||
Cli.type(character, interactionData.getInteractedWithText());
|
||||
return;
|
||||
}
|
||||
|
||||
Cli.type(character, interactionData.getStartText());
|
||||
|
||||
switch (character.getInteraction()) {
|
||||
case Farmer -> {
|
||||
System.out.println("Inplement interaction with farmer");
|
||||
var options = new String[] {"Kámen", "Nůžky", "Papír"};
|
||||
var selected = Cli.selectOptionIndex(Arrays.stream(options).toList());
|
||||
|
||||
var requestData = new InteractionRequest(String.valueOf(selected), character.getId());
|
||||
|
||||
var res = apiService.interact(playerKey, requestData).execute();
|
||||
var interactionResponse = res.body().getData().get();
|
||||
|
||||
Cli.type(character, interactionResponse.getResponseText());
|
||||
|
||||
if (interactionResponse.isSuccess()) {
|
||||
Cli.gotItems(interactionResponse.getItems());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package cz.jzitnik.utils;
|
||||
|
||||
import cz.jzitnik.api.types.Character;
|
||||
import cz.jzitnik.api.types.Item;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
@ -53,15 +56,11 @@ public class Cli {
|
||||
int padding = (totalWidth - text.length()) / 2;
|
||||
|
||||
StringBuilder sb = new StringBuilder(totalWidth);
|
||||
for (int i = 0; i < padding; i++) {
|
||||
sb.append("=");
|
||||
}
|
||||
sb.append("=".repeat(Math.max(0, padding)));
|
||||
sb.append(" ");
|
||||
sb.append(text);
|
||||
sb.append(" ");
|
||||
for (int i = 0; i < padding; i++) {
|
||||
sb.append("=");
|
||||
}
|
||||
sb.append("=".repeat(Math.max(0, padding)));
|
||||
|
||||
while (sb.length() < totalWidth) {
|
||||
sb.insert(0, "=");
|
||||
@ -76,13 +75,9 @@ public class Cli {
|
||||
int padding = (totalWidth - text.length()) / 2;
|
||||
|
||||
StringBuilder sb = new StringBuilder(totalWidth);
|
||||
for (int i = 0; i < padding; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(" ".repeat(Math.max(0, padding)));
|
||||
sb.append(text);
|
||||
for (int i = 0; i < padding; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(" ".repeat(Math.max(0, padding)));
|
||||
|
||||
while (sb.length() < totalWidth) {
|
||||
sb.insert(0, " ");
|
||||
@ -125,6 +120,18 @@ public class Cli {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
public static void gotItems(List<Item> items) {
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var itemsMapped = items.stream().map(Item::toString).toList();
|
||||
Cli.info("Dostal jste: " + String.join(", ", itemsMapped));
|
||||
}
|
||||
|
||||
public static void type(Character character, String text) {
|
||||
Cli.type(Cli.Colors.YELLOW + character.getName() + ": " + Cli.Colors.RESET + text);
|
||||
}
|
||||
public static void type(String text) {
|
||||
Cli.typeText(Cli.wrapText(text));
|
||||
}
|
||||
@ -171,37 +178,6 @@ public class Cli {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a menu with the provided options and returns the selected option.
|
||||
*
|
||||
* @param options Array of options to display to the user.
|
||||
* @return The selected option as a String.
|
||||
*/
|
||||
public static int selectOptionIndex(String[] options) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int choice;
|
||||
|
||||
System.out.println("Vyberte možnost:");
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
System.out.println((i + 1) + ". " + options[i]);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
System.out.print("Vložte číslo možnosti: ");
|
||||
if (scanner.hasNextInt()) {
|
||||
choice = scanner.nextInt();
|
||||
if (choice >= 1 && choice <= options.length) {
|
||||
return choice - 1;
|
||||
} else {
|
||||
System.out.println("Neplatní možnost, vyberte číslo mezi 1 a " + options.length);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Neplatný vstup, vložte číslo.");
|
||||
scanner.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a menu with the provided options and returns the selected option.
|
||||
*
|
||||
|
Reference in New Issue
Block a user