feat: Implemented interactions and rooms

Now you can interact with characters and go between rooms
This commit is contained in:
2024-12-21 16:08:05 +01:00
parent 6786eb1be8
commit ce1710cfef
22 changed files with 510 additions and 105 deletions

View File

@ -1,5 +1,6 @@
package cz.jzitnik.chronos.controllers;
import cz.jzitnik.chronos.entities.Interaction;
import cz.jzitnik.chronos.entities.Item;
import cz.jzitnik.chronos.interactions.InteractionService;
import cz.jzitnik.chronos.payload.requests.InteractionRequest;
@ -27,6 +28,27 @@ public class CharacterController {
@Autowired
private CharacterRepository characterRepository;
@GetMapping("/interaction")
@CheckUser
public ResponseEntity<UnifiedResponse<Interaction, Error>> getInteractionData(@RequestParam String playerKey, @RequestParam Long characterId) {
var player = playerRepository.findByPlayerKey(playerKey).get();
var characterOptional = characterRepository.findById(characterId);
if (characterOptional.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(UnifiedResponse.failure(null));
}
if (!characterOptional.get().getRoom().equals(player.getCurrentRoom())) {
return ResponseEntity.badRequest().body(UnifiedResponse.failure(null));
}
var interaction = characterOptional.get().getInteractionData();
return ResponseEntity.ok(
UnifiedResponse.success(interaction)
);
}
@PostMapping("/interact")
@CheckUser
public ResponseEntity<UnifiedResponse<InteractionResponse, Error>> interact(@RequestParam String playerKey, @RequestBody InteractionRequest interactionRequest) {

View File

@ -1,11 +1,15 @@
package cz.jzitnik.chronos.controllers;
import cz.jzitnik.chronos.entities.Player;
import cz.jzitnik.chronos.payload.errors.ItemNotUsableException;
import cz.jzitnik.chronos.payload.errors.NotFoundError;
import cz.jzitnik.chronos.payload.requests.PlayerNameRequest;
import cz.jzitnik.chronos.payload.responses.UnifiedResponse;
import cz.jzitnik.chronos.repository.GameRepository;
import cz.jzitnik.chronos.repository.ItemRepository;
import cz.jzitnik.chronos.repository.PlayerRepository;
import cz.jzitnik.chronos.services.GameService;
import cz.jzitnik.chronos.utils.anotations.CheckUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -24,6 +28,9 @@ public class PlayerController {
@Autowired
private GameService gameService;
@Autowired
private ItemRepository itemRepository;
@PostMapping("/new")
public ResponseEntity<UnifiedResponse<String, Error>> newPlayer(@RequestParam String gameKey, @RequestBody PlayerNameRequest playerNameRequest) {
var gameOptional = gameRepository.findByGameKey(gameKey);
@ -42,4 +49,42 @@ public class PlayerController {
UnifiedResponse.success(player.getPlayerKey())
);
}
@GetMapping("/me")
@CheckUser
public ResponseEntity<UnifiedResponse<Player, Error>> getMe(@RequestParam String playerKey) {
var player = playerRepository.findByPlayerKey(playerKey).get();
return ResponseEntity.ok(UnifiedResponse.success(player));
}
@PostMapping("/use_item")
@CheckUser
public ResponseEntity<UnifiedResponse<Object, Error>> useItem(@RequestParam String playerKey, @RequestParam Long itemId) {
var player = playerRepository.findByPlayerKey(playerKey).get();
var itemOptional = itemRepository.findById(itemId);
if (itemOptional.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(UnifiedResponse.failure(new NotFoundError("Item was not found!")));
}
var item = itemOptional.get();
if (!player.getInventory().contains(item)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(UnifiedResponse.failure(new Error("You do not own this item!")));
}
// Use the item
try {
item.getItemType().useItem(player);
} catch (ItemNotUsableException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(UnifiedResponse.failure(e.toError()));
}
playerRepository.save(player);
itemRepository.delete(item);
return ResponseEntity.ok(UnifiedResponse.success(null));
}
}

View File

@ -2,6 +2,7 @@ package cz.jzitnik.chronos.entities;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@ -24,4 +25,10 @@ public class Interaction {
@OneToOne(mappedBy = "interactionData")
@JsonIgnore
private Character character;
public Interaction(String startText, String interactedWithText, Character character) {
this.startText = startText;
this.interactedWithText = interactedWithText;
this.character = character;
}
}

View File

@ -1,6 +1,17 @@
package cz.jzitnik.chronos.entities;
import cz.jzitnik.chronos.payload.errors.ItemNotUsableException;
public enum ItemType {
KEY_FRAGMENT,
LUCK_POTION
LUCK_POTION;
public void useItem(Player player) throws ItemNotUsableException {
switch (this) {
case LUCK_POTION -> {
player.setLuck(true);
}
default -> throw new ItemNotUsableException("Item " + this + " is not usable");
}
}
}

View File

@ -10,6 +10,8 @@ import cz.jzitnik.chronos.repository.PlayerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Random;
@Service
@ -48,30 +50,42 @@ public class InteractionService {
player.setLuck(false);
if (playerWins) {
player.addItem(new Item(ItemType.KEY_FRAGMENT, player));
var item = new Item(ItemType.KEY_FRAGMENT, player);
player.addItem(item);
playerRepository.save(player);
character.setInteractedWith(true);
characterRepository.save(character);
return new InteractionResponse(true, "Vyhrál jsi, dostáváš jeden fragment klíče.");
var items = new ArrayList<Item>();
items.add(item);
return new InteractionResponse(true, "Vyhrál jsi, dostáváš ode mě jeden fragment klíče.", items);
} else {
return new InteractionResponse(false, "Prohrál jsi");
character.setInteractedWith(true);
characterRepository.save(character);
return new InteractionResponse(false, "Prohrál jsi. Žádný fragment klíče ode mě už nedostaneš. Zkus to u někoho jiného.", new ArrayList<>());
}
} else {
// Standard rock-paper-scissors game logic
if (userChoice == characterChoice) {
return new InteractionResponse(false, "Remíza, oběma hráčům se podařilo uhádnout.");
return new InteractionResponse(false, "Remíza. Jestli chceš můžeš si se mnou zahrát ještě jednou.", new ArrayList<>());
} else if ((userChoice == 0 && characterChoice == 2) || (userChoice == 1 && characterChoice == 0) || (userChoice == 2 && characterChoice == 1)) {
player.addItem(new Item(ItemType.KEY_FRAGMENT, player));
var item = new Item(ItemType.KEY_FRAGMENT, player);
player.addItem(item);
playerRepository.save(player);
character.setInteractedWith(true);
characterRepository.save(character);
return new InteractionResponse(true, "Vyhrál jsi, dostáváš jeden fragment klíče.");
var items = new ArrayList<Item>();
items.add(item);
return new InteractionResponse(true, "Vyhrál jsi, dostáváš ode mě jeden fragment klíče.", items);
} else {
return new InteractionResponse(false, "Prohrál jsi");
character.setInteractedWith(true);
characterRepository.save(character);
return new InteractionResponse(false, "Prohrál jsi. Žádný fragment klíče ode mě už nedostaneš. Zkus to u někoho jiného.", new ArrayList<>());
}
}
});

View File

@ -0,0 +1,17 @@
package cz.jzitnik.chronos.payload.errors;
public class ItemNotUsableException extends Exception {
public static class ItemNotUsableError extends Error {
public ItemNotUsableError (String message) {
super(message);
}
}
public ItemNotUsableException(String message) {
super(message);
}
public ItemNotUsableError toError() {
return new ItemNotUsableError(super.getMessage());
}
}

View File

@ -1,13 +1,17 @@
package cz.jzitnik.chronos.payload.responses;
import cz.jzitnik.chronos.entities.Item;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
public class InteractionResponse {
private boolean success;
private String responseText;
private List<Item> items;
}

View File

@ -0,0 +1,10 @@
package cz.jzitnik.chronos.repository;
import cz.jzitnik.chronos.entities.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ItemRepository extends JpaRepository<Item, Long> {
}

View File

@ -38,9 +38,14 @@ public class InitGameService {
var farmar = new Character(
"Farmář",
stodola,
"Ahoj já jsem farmář a budeš se mnou hrát kámen nůžky papír."
"Ahoj já jsem farmář a budeš se mnou hrát kámen nůžky papír. Dialog doplnit."
);
farmar.setInteraction(Interaction.Farmer);
farmar.setInteractionData(new cz.jzitnik.chronos.entities.Interaction(
"Tak si zahrajeme kámen nůžky papír",
"Se mnou jsi již hrál kámen nůžky papír. Znovu už ti fragment klíče nedám",
farmar
));
stodola_characters.add(farmar);
stodola.setCharacters(stodola_characters);