refactor: make GameLogic.checkRequest use error pattern and simple check methods
This commit is contained in:
parent
05f4751a2c
commit
d36466a5a0
@ -120,6 +120,20 @@ public class IntVector2 implements Serializable {
|
|||||||
return distance(v.x, v.y);
|
return distance(v.x, v.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float distanceManhattan(int x1, int y1, int x2, int y2) {
|
||||||
|
final float x_d = x2 - x1;
|
||||||
|
final float y_d = y2 - y1;
|
||||||
|
return Math.abs(x_d) + Math.abs(y_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float distanceManhattan(int x, int y) {
|
||||||
|
return IntVector2.distanceManhattan(this.x, this.y, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float distanceManhattan(IntVector2 v) {
|
||||||
|
return distanceManhattan(v.x, v.y);
|
||||||
|
}
|
||||||
|
|
||||||
public static float distance2(int x1, int y1, int x2, int y2) {
|
public static float distance2(int x1, int y1, int x2, int y2) {
|
||||||
final float x_d = x2 - x1;
|
final float x_d = x2 - x1;
|
||||||
final float y_d = y2 - y1;
|
final float y_d = y2 - y1;
|
||||||
|
@ -6,6 +6,6 @@ public enum StatType {
|
|||||||
HP,
|
HP,
|
||||||
/** Represents the movement points of a character */
|
/** Represents the movement points of a character */
|
||||||
MP,
|
MP,
|
||||||
/** Represents thr action points of a character */
|
/** Represents the action points of a character */
|
||||||
AP
|
AP
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,34 @@ public class EntityManager {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any entity on a position blocks vision.
|
||||||
|
* @param pos The position to check on
|
||||||
|
* @return Whether or not anything on the position blocks vision
|
||||||
|
*/
|
||||||
|
public boolean blocksVision(IntVector2 pos) {
|
||||||
|
for(Entity entity: entities) {
|
||||||
|
if(entity.getPosition() == pos && entity.blocksVision()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any entity on a position blocks movement.
|
||||||
|
* @param pos The position to check on
|
||||||
|
* @return Whether or not anything on the position blocks movement
|
||||||
|
*/
|
||||||
|
public boolean blocksMovement(IntVector2 pos) {
|
||||||
|
for(Entity entity: entities) {
|
||||||
|
if(entity.getPosition() == pos && entity.blocksMovement()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all entities inside the list.
|
* Iterates over all entities inside the list.
|
||||||
* @return An iterator over every {@link Entity}
|
* @return An iterator over every {@link Entity}
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
||||||
|
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.IntVector2;
|
||||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||||
import uulm.teamname.marvelous.gamelibrary.entities.Entity;
|
import uulm.teamname.marvelous.gamelibrary.entities.Entity;
|
||||||
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
import uulm.teamname.marvelous.gamelibrary.entities.Character;
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.entities.StatType;
|
||||||
import uulm.teamname.marvelous.gamelibrary.events.*;
|
import uulm.teamname.marvelous.gamelibrary.events.*;
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
||||||
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
|
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
|
||||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||||
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.Line2D;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/** Contains game logic handling. */
|
/** Contains game logic handling. */
|
||||||
@ -80,8 +87,7 @@ class GameLogic {
|
|||||||
.setTargetField(data.originField)
|
.setTargetField(data.originField)
|
||||||
.setAmount(1)
|
.setAmount(1)
|
||||||
.type(EventType.ConsumedAPEvent));
|
.type(EventType.ConsumedAPEvent));
|
||||||
//TODO: add infinity stone use effect in GameLogic.executeRequest
|
//TODO: add infinity stone usage effect in GameLogic.executeRequest
|
||||||
|
|
||||||
}
|
}
|
||||||
case DisconnectRequest -> {
|
case DisconnectRequest -> {
|
||||||
result.add(new GameEvent()
|
result.add(new GameEvent()
|
||||||
@ -99,70 +105,51 @@ class GameLogic {
|
|||||||
* @return Whether or not the request is valid
|
* @return Whether or not the request is valid
|
||||||
*/
|
*/
|
||||||
public static boolean checkRequest(GameState state, Request request) {
|
public static boolean checkRequest(GameState state, Request request) {
|
||||||
//TODO: refactor this using errors
|
|
||||||
try {
|
try {
|
||||||
switch(request.type) {
|
switch(request.type) {
|
||||||
case MeleeAttackRequest, RangedAttackRequest -> {
|
case MeleeAttackRequest, RangedAttackRequest -> {
|
||||||
CharacterRequest data = (CharacterRequest)request;
|
CharacterRequest data = (CharacterRequest)request;
|
||||||
|
|
||||||
Entity originEntity = state.entities.findEntity(data.originEntity);
|
Character origin = getCharacter(state, data.originField, data.originEntity);
|
||||||
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
Character target = getCharacter(state, data.targetField, data.targetEntity);
|
||||||
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()) {
|
requireAlive(origin);
|
||||||
return false;
|
requireAlive(target);
|
||||||
}
|
requireAP(origin, 1);
|
||||||
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) {
|
if(request.type == RequestType.MeleeAttackRequest) {
|
||||||
return false;
|
if(origin.meleeDamage != data.value) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
if(data.originField.distanceManhattan(data.targetField) > 1) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}else if(request.type == RequestType.RangedAttackRequest) {
|
||||||
|
if(origin.rangedDamage != data.value) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
if(data.originField.distanceManhattan(data.targetField) > origin.attackRange) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
if(data.originField.distanceManhattan(data.targetField) <= 1) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
requireLineOfSight(state, data.originField, data.targetField);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check line of sight in GameLogic.checkRequest
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MoveRequest -> {
|
case MoveRequest -> {
|
||||||
CharacterRequest data = (CharacterRequest)request;
|
CharacterRequest data = (CharacterRequest)request;
|
||||||
|
|
||||||
Entity originEntity = state.entities.findEntity(data.originEntity);
|
Character origin = getCharacter(state, data.originField, 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()) {
|
requireAlive(origin);
|
||||||
return false;
|
requireMP(origin, 1);
|
||||||
}
|
verifyCoordinates(state, data.targetField);
|
||||||
|
|
||||||
for(Entity entity: state.entities.findByPosition(data.targetField)) {
|
if(state.entities.blocksMovement(data.targetField)) {
|
||||||
if(entity.blocksMovement()) {
|
throw new InvalidRequestException();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(origin.hp.getValue() <= 0 || !origin.isActive()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(origin.mp.getValue() < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -170,59 +157,29 @@ class GameLogic {
|
|||||||
case ExchangeInfinityStoneRequest -> {
|
case ExchangeInfinityStoneRequest -> {
|
||||||
CharacterRequest data = (CharacterRequest)request;
|
CharacterRequest data = (CharacterRequest)request;
|
||||||
|
|
||||||
Entity originEntity = state.entities.findEntity(data.originEntity);
|
Character origin = getCharacter(state, data.originField, data.originEntity);
|
||||||
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
Character target = getCharacter(state, data.targetField, data.targetEntity);
|
||||||
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()) {
|
requireAlive(origin);
|
||||||
return false;
|
requireAlive(target);
|
||||||
}
|
requireAP(origin, 1);
|
||||||
if(origin.ap.getValue() < 1) {
|
requireInfinityStone(origin, data.stoneType);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(target.hp.getValue() <= 0 || !target.isActive()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!origin.inventory.hasStone(data.stoneType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case UseInfinityStoneRequest -> {
|
case UseInfinityStoneRequest -> {
|
||||||
CharacterRequest data = (CharacterRequest)request;
|
CharacterRequest data = (CharacterRequest)request;
|
||||||
|
|
||||||
Entity originEntity = state.entities.findEntity(data.originEntity);
|
Character origin = getCharacter(state, data.originField, data.originEntity);
|
||||||
if(originEntity == null || originEntity.getPosition() != data.originField || !(originEntity instanceof Character)) {
|
Character target = getCharacter(state, data.targetField, data.targetEntity);
|
||||||
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()) {
|
requireAlive(origin);
|
||||||
return false;
|
requireAP(origin, 1);
|
||||||
}
|
requireInfinityStone(origin, data.stoneType);
|
||||||
if(origin.ap.getValue() < 1) {
|
|
||||||
return false;
|
//TODO: properly verify UseInfinityStoneRequest in GameLogic.checkRequest
|
||||||
}
|
|
||||||
if(!target.isActive()) {
|
if(!target.isActive()) {
|
||||||
return false;
|
throw new InvalidRequestException();
|
||||||
}
|
|
||||||
|
|
||||||
if(!origin.inventory.hasStone(data.stoneType)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -234,13 +191,98 @@ class GameLogic {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e) {
|
}catch(Exception ignored) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a {@link Character} for a {@link Request}.
|
||||||
|
* @param state The game state to use
|
||||||
|
* @param position The requested position
|
||||||
|
* @param entityID The requested {@link EntityID}
|
||||||
|
* @return The found character
|
||||||
|
* @throws InvalidRequestException if the character is invalid or not found
|
||||||
|
*/
|
||||||
|
private static Character getCharacter(GameState state, IntVector2 position, EntityID entityID) throws InvalidRequestException {
|
||||||
|
Entity entity = state.entities.findEntity(entityID);
|
||||||
|
if(entity == null || entity.getPosition() != position || !(entity instanceof Character)) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (Character)entity;
|
||||||
|
}catch(Exception ignored) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a {@link Character} is alive.
|
||||||
|
*/
|
||||||
|
private static void requireAlive(Character entity) throws InvalidRequestException {
|
||||||
|
if(entity.hp.getValue() <= 0 || !entity.isActive()) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a {@link Character} has enough {@link StatType#AP}.
|
||||||
|
*/
|
||||||
|
private static void requireAP(Character entity, int ap) throws InvalidRequestException {
|
||||||
|
if(entity.ap.getValue() < ap) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a {@link Character} has enough {@link StatType#MP}.
|
||||||
|
*/
|
||||||
|
private static void requireMP(Character entity, int mp) throws InvalidRequestException {
|
||||||
|
if(entity.mp.getValue() < mp) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a {@link Character} has the required {@link StoneType}.
|
||||||
|
*/
|
||||||
|
private static void requireInfinityStone(Character entity, StoneType stone) throws InvalidRequestException {
|
||||||
|
if(!entity.inventory.hasStone(stone)) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that coordinates are within the playing area.
|
||||||
|
*/
|
||||||
|
private static void verifyCoordinates(GameState state, IntVector2 position) throws InvalidRequestException {
|
||||||
|
if(position.getX() < 0 || position.getX() >= state.mapSize.getX() || position.getY() < 0 || position.getY() >= state.mapSize.getY()) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that there is a line of sight between two positions.
|
||||||
|
*/
|
||||||
|
private static void requireLineOfSight(GameState state, IntVector2 start, IntVector2 end) throws InvalidRequestException {
|
||||||
|
//naive code for the win!!! \o/
|
||||||
|
//at least its early exit and probably only O(ln(n*m))
|
||||||
|
//TODO: implement proper line rasterization algorithm in GameLogic.requireLineOfSight
|
||||||
|
Line2D line = new Line2D.Float(start.getX(), start.getY(), end.getX(), end.getY());
|
||||||
|
for(int i = start.getX(); i <= end.getX(); i++) {
|
||||||
|
for(int j = start.getY(); j <= end.getY(); j++) {
|
||||||
|
var cell = new Rectangle.Float(i - 0.5f, j - 0.5f, 1, 1);
|
||||||
|
if(line.intersects(cell)) {
|
||||||
|
if(state.entities.blocksVision(new IntVector2(i, j))) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies an {@link Event} to a {@link GameState}.
|
* Applies an {@link Event} to a {@link GameState}.
|
||||||
* @param state The game state to apply to
|
* @param state The game state to apply to
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package uulm.teamname.marvelous.gamelibrary.gamelogic;
|
||||||
|
|
||||||
|
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||||
|
|
||||||
|
/** Represents an exception thrown when a {@link Request} is invalid. */
|
||||||
|
public class InvalidRequestException extends Exception {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user