feat: add handling and checking for UseInfinityStoneRequest, fix: use proper distance calculations for some checks

This commit is contained in:
punchready 2021-05-11 03:50:24 +02:00
parent 4203092f9a
commit 1a98f25139
3 changed files with 179 additions and 14 deletions

View File

@ -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;

View File

@ -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<Entity> entities = new HashSet<>();
/** A set of all currently used {@link Rock} entity ids */
private final HashSet<Integer> 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);
}

View File

@ -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;