diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/IntVector2.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/IntVector2.java index 5c4e8c9..c6dc01c 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/IntVector2.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/IntVector2.java @@ -120,6 +120,23 @@ public class IntVector2 implements Serializable { return distance(v.x, v.y); } + /** Equivalent to the minimum amount of king moves to go from A to B in chess */ + public static float distanceChebyshev(int x1, int y1, int x2, int y2) { + final float x_d = x2 - x1; + final float y_d = y2 - y1; + return Math.max(Math.abs(x_d), Math.abs(y_d)); + } + + /** Equivalent to the minimum amount of king moves to go from A to B in chess */ + public float distanceChebyshev(int x, int y) { + return IntVector2.distanceChebyshev(this.x, this.y, x, y); + } + + /** Equivalent to the minimum amount of king moves to go from A to B in chess */ + public float distanceChebyshev(IntVector2 v) { + return distanceChebyshev(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; diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/EntityManager.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/EntityManager.java index 33300ca..067a9ce 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/EntityManager.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/EntityManager.java @@ -3,9 +3,10 @@ package uulm.teamname.marvelous.gamelibrary.gamelogic; import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.entities.Entity; import uulm.teamname.marvelous.gamelibrary.entities.EntityID; +import uulm.teamname.marvelous.gamelibrary.entities.EntityType; +import uulm.teamname.marvelous.gamelibrary.entities.Rock; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; @@ -14,6 +15,9 @@ public class EntityManager { /** The internal collection of {@link Entity}s */ private final HashSet entities = new HashSet<>(); + /** A set of all currently used {@link Rock} entity ids */ + private final HashSet usedRockSlots = new HashSet<>(); + /** * Takes over all the entities from a different {@link EntityManager}. * @param other The entity list to take the data from @@ -23,6 +27,21 @@ public class EntityManager { for(Entity entity: other.entities) { entities.add(entity.clone()); } + + usedRockSlots.clear(); + usedRockSlots.addAll(other.usedRockSlots); + } + + /** + * Finds an unused {@link Rock} entity id + * @return The first free id + */ + public int findFreeRockSlot() { + int i = 0; + while(usedRockSlots.contains(i)) { + i++; + } + return i; } /** @@ -30,6 +49,7 @@ public class EntityManager { */ public void clear() { entities.clear(); + usedRockSlots.clear(); } /** @@ -37,6 +57,9 @@ public class EntityManager { * @param entity The {@link Entity} to add */ public void addEntity(Entity entity) { + if(entity.id.isSameType(EntityType.Rocks)) { + usedRockSlots.add(entity.id.id); + } entities.add(entity); } @@ -45,7 +68,9 @@ public class EntityManager { * @param entities The entities to add */ public void addEntities(Entity... entities) { - this.entities.addAll(Arrays.asList(entities)); + for(Entity e: entities) { + this.addEntity(e); + } } /** @@ -53,6 +78,9 @@ public class EntityManager { * @param entity The {@link Entity} to remove */ public boolean removeEntity(Entity entity) { + if(entity.id.isSameType(EntityType.Rocks)) { + usedRockSlots.remove(entity.id.id); + } return entities.remove(entity); } diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java index 3e20323..ee34261 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java @@ -2,11 +2,8 @@ package uulm.teamname.marvelous.gamelibrary.gamelogic; import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.Tuple; -import uulm.teamname.marvelous.gamelibrary.entities.Entity; +import uulm.teamname.marvelous.gamelibrary.entities.*; 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.Event; import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest; @@ -86,7 +83,78 @@ class GameLogic { .withTargetField(data.originField) .withAmount(1) .buildEntityEvent()); - //TODO: add infinity stone usage effect in GameLogic.executeRequest + + if(request.type == RequestType.UseInfinityStoneRequest) { + switch(((CharacterRequest) request).stoneType) { + case SpaceStone -> { + result.add(new EventBuilder(EventType.MoveEvent) + .withOriginEntity(data.originEntity) + .withOriginField(data.originField) + .withTargetField(data.targetField) + .buildCharacterEvent()); + } + case MindStone -> { + //TODO: mind stone effect ??????? + } + case RealityStone -> { + if(data.originEntity == data.targetEntity) { // => place stone + //TODO: use config values + result.add(new EventBuilder(EventType.SpawnEntityEvent) + .withTargetField(data.targetField) + .withEntity(new Rock(new EntityID(EntityType.Rocks, state.entities.findFreeRockSlot()), data.targetField, 100)) + .buildEntityEvent()); + }else { // => destroy stone + result.add(new EventBuilder(EventType.DestroyedEntityEvent) + .withTargetField(data.targetField) + .withTargetEntity(data.targetEntity) + .buildEntityEvent()); + } + } + case PowerStone -> { + Character origin = (Character)state.entities.findEntity(data.originEntity); + int dmg = (int)Math.round(origin.hp.getValue() * 0.1); + if(origin.hp.getValue() != 1 && dmg > 0) { + result.add(new EventBuilder(EventType.TakenDamageEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(dmg) + .buildEntityEvent()); + } + result.add(new EventBuilder(EventType.TakenDamageEvent) + .withTargetEntity(data.targetEntity) + .withTargetField(data.targetField) + .withAmount(data.value) + .buildEntityEvent()); + } + case TimeStone -> { + Character origin = (Character)state.entities.findEntity(data.originEntity); + int ap = origin.ap.max - origin.ap.getValue(); + if(ap < 0) { + result.add(new EventBuilder(EventType.ConsumedAPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(ap) + .buildEntityEvent()); + } + int mp = origin.mp.max - origin.mp.getValue(); + if(mp < 0) { + result.add(new EventBuilder(EventType.ConsumedMPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(mp) + .buildEntityEvent()); + } + } + case SoulStone -> { + Character target = (Character)state.entities.findEntity(data.targetEntity); + result.add(new EventBuilder(EventType.HealedEvent) + .withTargetEntity(data.targetEntity) + .withTargetField(data.targetField) + .withAmount(target.hp.max) + .buildEntityEvent()); + } + } + } } case Req -> { result.add(new EventBuilder(EventType.GameStateEvent) @@ -126,17 +194,17 @@ class GameLogic { if(origin.meleeDamage != data.value) { throw new InvalidRequestException(); } - if(data.originField.distanceManhattan(data.targetField) > 1) { + if(data.originField.distanceChebyshev(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) { + if(data.originField.distanceChebyshev(data.targetField) > origin.attackRange) { throw new InvalidRequestException(); } - if(data.originField.distanceManhattan(data.targetField) <= 1) { + if(data.originField.distanceChebyshev(data.targetField) <= 1) { throw new InvalidRequestException(); } requireLineOfSight(state, data.originField, data.targetField); @@ -153,6 +221,10 @@ class GameLogic { requireMP(origin, 1); verifyCoordinates(state, data.targetField); + if(data.originField.distanceChebyshev(data.targetField) != 1) { + throw new InvalidRequestException(); + } + if(state.entities.blocksMovement(data.targetField)) { throw new InvalidRequestException(); } @@ -170,21 +242,69 @@ class GameLogic { requireAP(origin, 1); requireInfinityStone(origin, data.stoneType); + if(data.originField.distanceChebyshev(data.targetField) != 1) { + throw new InvalidRequestException(); + } + return true; } case UseInfinityStoneRequest -> { CharacterRequest data = (CharacterRequest)request; Character origin = getCharacter(state, data.originField, data.originEntity); - Character target = getCharacter(state, data.targetField, data.targetEntity); requireAlive(origin); requireAP(origin, 1); requireInfinityStone(origin, data.stoneType); - //TODO: properly verify UseInfinityStoneRequest in GameLogic.checkRequest - if(!target.isActive()) { - throw new InvalidRequestException(); + switch(((CharacterRequest) request).stoneType) { + case SpaceStone -> { + verifyCoordinates(state, data.targetField); + + if(state.entities.blocksMovement(data.targetField)) { + throw new InvalidRequestException(); + } + } + case MindStone -> { + //TODO: mind stone requirements (config, etc) ??????? + } + case RealityStone -> { + if(data.originEntity == data.targetEntity) { // => place stone + if(state.entities.findByPosition(data.targetField).size() != 0) { + throw new InvalidRequestException(); + } + }else { // => destroy stone + boolean hasRock = false; + for(Entity entity: state.entities.findByPosition(data.targetField)) { + if(entity.id.isSameType(EntityType.Rocks)) { + hasRock = true; + break; + } + } + if(!hasRock) { + throw new InvalidRequestException(); + } + } + } + case PowerStone -> { + Character target = getCharacter(state, data.targetField, data.targetEntity); + + requireAlive(target); + + if(origin.rangedDamage * 2 != data.value) { + throw new InvalidRequestException(); + } + } + case TimeStone -> { + // "👍 i approve" - the server + } + case SoulStone -> { + Character target = getCharacter(state, data.targetField, data.targetEntity); + + if(target.hp.getValue() != 0) { + throw new InvalidRequestException(); + } + } } return true;