2021-04-29 17:15:29 +00:00
|
|
|
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
|
|
|
|
|
|
|
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
2021-05-01 22:03:03 +00:00
|
|
|
import uulm.teamname.marvelous.gamelibrary.entities.Entity;
|
|
|
|
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
2021-05-01 21:06:22 +00:00
|
|
|
import uulm.teamname.marvelous.gamelibrary.events.*;
|
|
|
|
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
|
2021-04-29 17:15:29 +00:00
|
|
|
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
2021-05-01 21:06:22 +00:00
|
|
|
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
2021-04-29 17:15:29 +00:00
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
2021-04-30 18:54:34 +00:00
|
|
|
/** Contains game logic handling. */
|
2021-04-29 17:15:29 +00:00
|
|
|
class GameLogic {
|
2021-04-30 18:54:34 +00:00
|
|
|
/**
|
2021-05-02 12:38:03 +00:00
|
|
|
* Produces resulting {@link Event}s from a given {@link Request}.
|
|
|
|
* @param state The game state to execute on
|
|
|
|
* @param request The request to execute
|
2021-04-30 18:54:34 +00:00
|
|
|
* @return The list of resulting events
|
2021-04-29 17:15:29 +00:00
|
|
|
*/
|
2021-05-02 12:38:03 +00:00
|
|
|
public static ArrayList<Event> executeRequest(GameState state, Request request) {
|
2021-05-01 21:06:22 +00:00
|
|
|
ArrayList<Event> result = new ArrayList<>();
|
2021-04-29 17:15:29 +00:00
|
|
|
|
2021-05-02 12:38:03 +00:00
|
|
|
//TODO: refactor for EventBuilder in GameLogic.executeRequest
|
2021-05-01 21:06:22 +00:00
|
|
|
switch(request.type) {
|
|
|
|
case MeleeAttackRequest, RangedAttackRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
result.add(new CharacterEvent()
|
|
|
|
.setOriginEntity(data.originEntity)
|
|
|
|
.setTargetEntity(data.targetEntity)
|
|
|
|
.setOriginField(data.originField)
|
|
|
|
.setTargetField(data.targetField)
|
|
|
|
.setAmount(data.value)
|
|
|
|
.type(request.type == RequestType.MeleeAttackRequest ? EventType.MeleeAttackEvent : EventType.RangedAttackEvent));
|
|
|
|
result.add(new EntityEvent()
|
|
|
|
.setTargetEntity(data.originEntity)
|
|
|
|
.setTargetField(data.originField)
|
|
|
|
.setAmount(1)
|
|
|
|
.type(EventType.ConsumedAPEvent));
|
|
|
|
result.add(new EntityEvent()
|
|
|
|
.setTargetEntity(data.targetEntity)
|
|
|
|
.setTargetField(data.targetField)
|
|
|
|
.setAmount(data.value)
|
|
|
|
.type(EventType.TakenDamageEvent));
|
|
|
|
}
|
|
|
|
case MoveRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
result.add(new CharacterEvent()
|
|
|
|
.setOriginEntity(data.originEntity)
|
|
|
|
.setOriginField(data.originField)
|
|
|
|
.setTargetField(data.targetField)
|
|
|
|
.type(EventType.MoveEvent));
|
|
|
|
result.add(new EntityEvent()
|
|
|
|
.setTargetEntity(data.originEntity)
|
|
|
|
.setTargetField(data.targetField) //when this event gets handled, the character already moved to the target field
|
|
|
|
.setAmount(1)
|
|
|
|
.type(EventType.ConsumedMPEvent));
|
2021-05-02 12:38:03 +00:00
|
|
|
for(Entity entity: state.entities.findByPosition(data.targetField)) {
|
|
|
|
if(entity instanceof Character) {
|
|
|
|
result.add(new CharacterEvent()
|
|
|
|
.setOriginEntity(entity.id)
|
|
|
|
.setOriginField(data.targetField)
|
|
|
|
.setTargetField(data.originField)
|
|
|
|
.type(EventType.MoveEvent));
|
|
|
|
break; //we should only have one character per field anyways
|
|
|
|
}
|
|
|
|
}
|
2021-05-01 21:06:22 +00:00
|
|
|
}
|
|
|
|
case ExchangeInfinityStoneRequest, UseInfinityStoneRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
result.add(new CharacterEvent()
|
|
|
|
.setOriginEntity(data.originEntity)
|
|
|
|
.setTargetEntity(data.targetEntity)
|
|
|
|
.setOriginField(data.originField)
|
|
|
|
.setTargetField(data.targetField)
|
|
|
|
.setStone(data.stoneType)
|
|
|
|
.type(request.type == RequestType.ExchangeInfinityStoneRequest ? EventType.ExchangeInfinityStoneEvent : EventType.UseInfinityStoneEvent));
|
|
|
|
result.add(new EntityEvent()
|
|
|
|
.setTargetEntity(data.originEntity)
|
|
|
|
.setTargetField(data.originField)
|
|
|
|
.setAmount(1)
|
|
|
|
.type(EventType.ConsumedAPEvent));
|
2021-05-02 12:38:03 +00:00
|
|
|
//TODO: add infinity stone use effect in GameLogic.executeRequest
|
2021-04-29 17:15:29 +00:00
|
|
|
|
2021-05-01 21:06:22 +00:00
|
|
|
}
|
|
|
|
case DisconnectRequest -> {
|
|
|
|
result.add(new GameEvent()
|
|
|
|
.type(EventType.DisconnectEvent));
|
|
|
|
}
|
2021-04-29 17:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-04-30 18:54:34 +00:00
|
|
|
/**
|
|
|
|
* Checks a {@link Request} for validity for a {@link GameState}.
|
|
|
|
* @param state The game state to check on
|
|
|
|
* @param request The request to validate
|
2021-04-29 17:15:29 +00:00
|
|
|
* @return Whether or not the request is valid
|
|
|
|
*/
|
|
|
|
public static boolean checkRequest(GameState state, Request request) {
|
2021-05-01 22:03:03 +00:00
|
|
|
//TODO: refactor this using errors
|
|
|
|
try {
|
|
|
|
switch(request.type) {
|
|
|
|
case MeleeAttackRequest, RangedAttackRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
|
|
|
|
Entity originEntity = state.entities.findEntity(data.originEntity);
|
|
|
|
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character origin = (Character)originEntity;
|
|
|
|
Entity targetEntity = state.entities.findEntity(data.targetEntity);
|
|
|
|
if(targetEntity == null || targetEntity.getPosition() != data.targetField || !(targetEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character target = (Character)targetEntity;
|
|
|
|
|
|
|
|
if(origin.hp.getValue() <= 0 || !origin.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(origin.ap.getValue() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(request.type == RequestType.MeleeAttackRequest && origin.meleeDamage != data.value) {
|
|
|
|
return false;
|
|
|
|
}else if(request.type == RequestType.RangedAttackRequest && origin.rangedDamage != data.value) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(target.hp.getValue() <= 0 || !target.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(request.type == RequestType.RangedAttackRequest && origin.getPosition().distance(target.getPosition()) > origin.attackRange) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: check line of sight in GameLogic.checkRequest
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case MoveRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
|
|
|
|
Entity originEntity = state.entities.findEntity(data.originEntity);
|
|
|
|
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character origin = (Character)originEntity;
|
|
|
|
|
|
|
|
if(data.targetField.getX() < 0 || data.targetField.getX() >= state.mapSize.getX() || data.targetField.getY() < 0 || data.targetField.getY() >= state.mapSize.getY()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(Entity entity: state.entities.findByPosition(data.targetField)) {
|
|
|
|
if(entity.blocksMovement()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(origin.hp.getValue() <= 0 || !origin.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(origin.mp.getValue() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case ExchangeInfinityStoneRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
|
|
|
|
Entity originEntity = state.entities.findEntity(data.originEntity);
|
|
|
|
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character origin = (Character)originEntity;
|
|
|
|
Entity targetEntity = state.entities.findEntity(data.targetEntity);
|
|
|
|
if(targetEntity == null || targetEntity.getPosition() != data.targetField || !(targetEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character target = (Character)targetEntity;
|
|
|
|
|
|
|
|
if(origin.hp.getValue() <= 0 || !origin.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(origin.ap.getValue() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(target.hp.getValue() <= 0 || !target.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!origin.inventory.hasStone(data.stoneType)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case UseInfinityStoneRequest -> {
|
|
|
|
CharacterRequest data = (CharacterRequest)request;
|
|
|
|
|
|
|
|
Entity originEntity = state.entities.findEntity(data.originEntity);
|
|
|
|
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character origin = (Character)originEntity;
|
|
|
|
Entity targetEntity = state.entities.findEntity(data.targetEntity);
|
|
|
|
if(targetEntity == null || targetEntity.getPosition() != data.targetField || !(targetEntity instanceof Character)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Character target = (Character)targetEntity;
|
|
|
|
|
|
|
|
if(origin.hp.getValue() <= 0 || !origin.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(origin.ap.getValue() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(!target.isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!origin.inventory.hasStone(data.stoneType)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
case DisconnectRequest -> {
|
|
|
|
//TODO: add check for DisconnectRequest in GameLogic.checkRequest
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}catch(Exception e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-29 17:15:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-30 18:54:34 +00:00
|
|
|
/**
|
|
|
|
* Applies an {@link Event} to a {@link GameState}.
|
|
|
|
* @param state The game state to apply to
|
|
|
|
* @param event The event to apply
|
2021-04-29 17:15:29 +00:00
|
|
|
*/
|
|
|
|
public static void applyEvent(GameState state, Event event) {
|
|
|
|
//TODO: implement GameLogic.applyEvent
|
|
|
|
}
|
|
|
|
|
2021-04-30 18:54:34 +00:00
|
|
|
/**
|
|
|
|
* Checks a {@link GameState} for the current overtime win condition.
|
|
|
|
* @param state The game state to check
|
|
|
|
* @return The {@link ParticipantType} that is currently winning the game according to overtime ruling
|
2021-04-29 17:15:29 +00:00
|
|
|
*/
|
|
|
|
public static ParticipantType checkWinConditions(GameState state) {
|
|
|
|
//TODO: GameLogic.checkWinConditions is kind of ugly
|
|
|
|
|
|
|
|
Tuple<ParticipantType, WinCondition> player1;
|
|
|
|
Tuple<ParticipantType, WinCondition> player2;
|
|
|
|
int value1;
|
|
|
|
int value2;
|
|
|
|
for(WinCondition condition: WinCondition.values()) {
|
|
|
|
player1 = new Tuple<ParticipantType, WinCondition>(ParticipantType.Player1, condition);
|
|
|
|
player2 = new Tuple<ParticipantType, WinCondition>(ParticipantType.Player2, condition);
|
|
|
|
value1 = 0;
|
|
|
|
value2 = 0;
|
|
|
|
|
|
|
|
if(state.winConditions.containsKey(player1)) {
|
|
|
|
value1 = state.winConditions.get(player1);
|
|
|
|
}
|
|
|
|
if(state.winConditions.containsKey(player2)) {
|
|
|
|
value2 = state.winConditions.get(player2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(value1 > value2) {
|
|
|
|
return ParticipantType.Player1;
|
|
|
|
}
|
|
|
|
if(value2 > value1) {
|
|
|
|
return ParticipantType.Player2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParticipantType.None;
|
|
|
|
}
|
|
|
|
}
|