From 3bb6d81b27e439da39c3f8467ff997c76471c3b9 Mon Sep 17 00:00:00 2001 From: punchready Date: Thu, 3 Jun 2021 02:19:10 +0200 Subject: [PATCH] refactor: restructure code and fix some events not being applied --- .../gamelibrary/gamelogic/GameInstance.java | 73 +-- .../gamelibrary/gamelogic/GameLogic.java | 523 +++++++++--------- .../gamelogic/GameStateManager.java | 95 ++-- .../gamelibrary/gamelogic/GameLogicTest.java | 44 +- 4 files changed, 371 insertions(+), 364 deletions(-) diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java index 494a76d..7f92375 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java @@ -27,7 +27,7 @@ public class GameInstance { /** Constructs a new {@link GameInstance}. */ public GameInstance(PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) { _state = new GameState(partyConfig, characterConfig, scenarioConfig); - this.state = new GameStateView(_state); + state = new GameStateView(_state); manager = new GameStateManager(_state); } @@ -36,16 +36,11 @@ public class GameInstance { * @param requests The requests to check * @return The list of resulting {@link Event}s or `null` if the check failed */ - public Optional> checkRequestsAndApply(Request... requests) { + public Optional> checkRequestsAndApply(ArrayList requests) { if(manager.processRequests(requests, true)) { ArrayList result = manager.apply(); - ArrayList result2 = manager.checkPostPhase(); - manager.applyEvents(result2); - - result.addAll(result2); - - result.add(GameLogic.buildGameStateEvent(_state)); + result.addAll(manager.checkPostPhase()); return Optional.of(result); } @@ -58,44 +53,10 @@ public class GameInstance { * @param requests The requests to check * @return Whether or not the given set of requests is valid */ - public boolean checkRequests(Request... requests) { + public boolean checkRequests(ArrayList requests) { return manager.processRequests(requests, false); } - /** - * Initializes and starts the game. Selected characters are given as a list of indices from the {@link CharacterConfig#characters} array. - * @param selectedCharacters1 The characters selected by player 1 - * @param selectedCharacters2 The characters selected by player 2 - * @return The list of resulting {@link Event}s - */ - public ArrayList startGame(ArrayList selectedCharacters1, ArrayList selectedCharacters2) { - ArrayList result = manager.initGame(selectedCharacters1, selectedCharacters2); - manager.applyEvents(result); - - ArrayList result2 = manager.startGame(); - manager.applyEvents(result2); - - result.addAll(result2); //do not merge this, result needs to be applied before startGame is called - - return result; - } - - /** - * Produces a {@link EventType#GamestateEvent} for the current {@link GameState}. - * @return The resulting event - */ - public Event getGameStateEvent() { - return GameLogic.buildGameStateEvent(_state); - } - - /** - * Restores the current {@link GameState} based on the given one. - * @param state The state to restore from - */ - public void setGameState(GameState state) { - _state.restore(state); - } - /** * Applies an array of {@link Event}s to the game state. * @param events The events to apply. @@ -104,8 +65,30 @@ public class GameInstance { manager.applyEvents(events); } - protected GameState getState() { - return _state; + /** + * Initializes and starts the game. Selected characters are given as a list of indices from the {@link CharacterConfig#characters} array. + * @param selectedCharacters1 The characters selected by player 1 + * @param selectedCharacters2 The characters selected by player 2 + * @return The list of resulting {@link Event}s + */ + public ArrayList startGame(ArrayList selectedCharacters1, ArrayList selectedCharacters2) { + return manager.startGame(selectedCharacters1, selectedCharacters2); + } + + /** + * Produces a {@link EventType#GamestateEvent} for the current {@link GameState}. + * @return The resulting event + */ + public Event getGameStateEvent() { + return manager.getGameStateEvent(); + } + + /** + * Restores the current {@link GameState} based on the given {@link EventType#GamestateEvent}. + * @param gameStateEvent The event to restore from + */ + public void applyGameStateEvent(Event gameStateEvent) { + manager.applyEvent(gameStateEvent); } @Override 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 d76c6dd..db398d4 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java @@ -19,199 +19,16 @@ import java.util.HashSet; import java.util.Random; /** Contains game logic handling. */ -class GameLogic { +public class GameLogic { private static final Random rand = new Random(); - /** - * Produces resulting {@link Event}s from a given {@link Request}. - * @param state The game state to execute on - * @param request The request to execute - * @return The list of resulting events - */ - public static ArrayList executeRequest(GameState state, Request request) { - ArrayList result = new ArrayList<>(); - - switch(request.type) { - case MeleeAttackRequest, RangedAttackRequest -> { - CharacterRequest data = (CharacterRequest)request; - result.add(new EventBuilder(request.type == RequestType.MeleeAttackRequest ? EventType.MeleeAttackEvent : EventType.RangedAttackEvent) - .withOriginEntity(data.originEntity) - .withTargetEntity(data.targetEntity) - .withOriginField(data.originField) - .withTargetField(data.targetField) - .withAmount(data.value) - .buildCharacterEvent()); - result.add(new EventBuilder(EventType.ConsumedAPEvent) - .withTargetEntity(data.originEntity) - .withTargetField(data.originField) - .withAmount(1) - .buildEntityEvent()); - result.add(new EventBuilder(EventType.TakenDamageEvent) - .withTargetEntity(data.targetEntity) - .withTargetField(data.targetField) - .withAmount(data.value) - .buildEntityEvent()); - } - case MoveRequest -> { - CharacterRequest data = (CharacterRequest)request; - result.add(new EventBuilder(EventType.MoveEvent) - .withOriginEntity(data.originEntity) - .withOriginField(data.originField) - .withTargetField(data.targetField) - .buildCharacterEvent()); - result.add(new EventBuilder(EventType.ConsumedMPEvent) - .withTargetEntity(data.originEntity) - .withTargetField(data.targetField) //when this event gets handled, the character already moved to the target field - .withAmount(1) - .buildEntityEvent()); - for(Entity entity: state.entities.findByPosition(data.targetField)) { - if(entity instanceof Character) { - result.add(new EventBuilder(EventType.MoveEvent) - .withOriginEntity(entity.id) - .withOriginField(data.targetField) - .withTargetField(data.originField) - .buildCharacterEvent()); - break; //we should only have one entity per field anyways - }else if(entity instanceof InfinityStone) { - result.add(new EventBuilder(EventType.DestroyedEntityEvent) - .withTargetField(data.targetField) - .withTargetEntity(entity.id) - .buildEntityEvent()); - break; //we should only have one entity per field anyways - } - } - } - case ExchangeInfinityStoneRequest, UseInfinityStoneRequest -> { - CharacterRequest data = (CharacterRequest)request; - result.add(new EventBuilder(request.type == RequestType.ExchangeInfinityStoneRequest ? EventType.ExchangeInfinityStoneEvent : EventType.UseInfinityStoneEvent) - .withOriginEntity(data.originEntity) - .withOriginField(data.originField) - .withTargetEntity(data.targetEntity) - .withTargetField(data.targetField) - .withStoneType(data.stoneType) - .buildCharacterEvent()); - result.add(new EventBuilder(EventType.ConsumedAPEvent) - .withTargetEntity(data.originEntity) - .withTargetField(data.originField) - .withAmount(1) - .buildEntityEvent()); - - 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 -> { - EntityType target = data.originEntity.type == EntityType.P1 ? EntityType.P2 : EntityType.P1; - for(IntVector2 pos: rasterize(data.originField, data.targetField, false, true)) { - for(Entity entity: state.entities.findByPosition(pos)) { - if(entity.id.isSameType(target)) { - result.add(new EventBuilder(EventType.TakenDamageEvent) - .withTargetEntity(entity.id) - .withTargetField(pos) - .withAmount(data.value) - .buildEntityEvent()); - } - } - } - } - case RealityStone -> { - if(data.originEntity == data.targetEntity) { // => place stone - 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.getMax() * 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.getValue() - origin.ap.getMax(); - if(ap < 0) { - result.add(new EventBuilder(EventType.ConsumedAPEvent) - .withTargetEntity(data.originEntity) - .withTargetField(data.originField) - .withAmount(ap) - .buildEntityEvent()); - } - int mp = origin.mp.getValue() - origin.mp.getMax(); - 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.getMax()) - .buildEntityEvent()); - } - } - } - } - case EndRoundRequest -> { - result.addAll(handleTurnEnd(state)); //why is it called end round request when it ends a turn... - } - case Req -> { - result.add(buildGameStateEvent(state)); - } - } - - return result; - } - - /** - * Builds a {@link EventType#GamestateEvent} for the given {@link GameState}. - * @param state The game state to use - * @return The resulting event - */ - public static Event buildGameStateEvent(GameState state) { - return new EventBuilder(EventType.GamestateEvent) - .withEntities(state.entities.export()) - .withTurnOrder(state.turnOrder.toArray(new EntityID[0])) - .withMapSize(state.mapSize) - .withActiveCharacter(state.activeCharacter) - .withStoneCooldowns(state.stoneCooldown.export()) - .withWinCondition(state.won) - .buildGameStateEvent(); - } - /** * Checks a {@link Request} for validity for a {@link GameState}. * @param state The game state to check on * @param request The request to validate * @return Whether or not the request is valid */ - public static boolean checkRequest(GameState state, Request request) { + protected static boolean checkRequest(GameState state, Request request) { try { switch(request.type) { case MeleeAttackRequest, RangedAttackRequest -> { @@ -478,74 +295,181 @@ class GameLogic { } } + /** - * Checks if a line of sight exists between the two positions - * @param state The game state to work on - * @param start The first position - * @param end The second position - * @return Whether or not the light of sight exists + * Produces resulting {@link Event}s from a given {@link Request}. + * @param state The game state to execute on + * @param request The request to execute + * @return The list of resulting events */ - private static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) { - for(IntVector2 pos: rasterize(start, end, false, false)) { - if(state.entities.blocksVision(pos)) { - return false; + protected static ArrayList executeRequest(GameState state, Request request) { + ArrayList result = new ArrayList<>(); + + switch(request.type) { + case MeleeAttackRequest, RangedAttackRequest -> { + CharacterRequest data = (CharacterRequest)request; + result.add(new EventBuilder(request.type == RequestType.MeleeAttackRequest ? EventType.MeleeAttackEvent : EventType.RangedAttackEvent) + .withOriginEntity(data.originEntity) + .withTargetEntity(data.targetEntity) + .withOriginField(data.originField) + .withTargetField(data.targetField) + .withAmount(data.value) + .buildCharacterEvent()); + result.add(new EventBuilder(EventType.ConsumedAPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(1) + .buildEntityEvent()); + result.add(new EventBuilder(EventType.TakenDamageEvent) + .withTargetEntity(data.targetEntity) + .withTargetField(data.targetField) + .withAmount(data.value) + .buildEntityEvent()); } - } - return true; - } - - /** - * Finds free neighbour options from a starting field. - * @param state The game state to work on - * @param start The starting position - * @return A list of free neighbour field options - */ - private static ArrayList getFreeNeighbour(GameState state, IntVector2 start) { - ArrayList options = new ArrayList<>(); - - if(start.getX() < 0 || start.getX() >= state.mapSize.getX() || start.getY() < 0 || start.getY() >= state.mapSize.getY()) { - return options; - } - - for(IntVector2 dir: IntVector2.CardinalDirections) { - if(state.entities.findByPosition(start.add(dir)).size() == 0) { - options.add(start.add(dir)); - } - } - - if(options.size() == 0) { - return getFreeNeighbour(state, start.add(IntVector2.CardinalDirections[rand.nextInt(IntVector2.CardinalDirections.length)])); - }else { - return options; - } - } - - /** - * Finds free field options. - * @param state The game state to work on - * @return A list of free field options - */ - private static ArrayList getFreeFields(GameState state) { - ArrayList options = new ArrayList<>(); - - for(int x = 0; x < state.mapSize.getX(); x++) { - for(int y = 0; y < state.mapSize.getY(); y++) { - IntVector2 pos = new IntVector2(x, y); - if(state.entities.findByPosition(pos).size() == 0) { - options.add(pos); + case MoveRequest -> { + CharacterRequest data = (CharacterRequest)request; + result.add(new EventBuilder(EventType.MoveEvent) + .withOriginEntity(data.originEntity) + .withOriginField(data.originField) + .withTargetField(data.targetField) + .buildCharacterEvent()); + result.add(new EventBuilder(EventType.ConsumedMPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.targetField) //when this event gets handled, the character already moved to the target field + .withAmount(1) + .buildEntityEvent()); + for(Entity entity: state.entities.findByPosition(data.targetField)) { + if(entity instanceof Character) { + result.add(new EventBuilder(EventType.MoveEvent) + .withOriginEntity(entity.id) + .withOriginField(data.targetField) + .withTargetField(data.originField) + .buildCharacterEvent()); + break; //we should only have one entity per field anyways + }else if(entity instanceof InfinityStone) { + result.add(new EventBuilder(EventType.DestroyedEntityEvent) + .withTargetField(data.targetField) + .withTargetEntity(entity.id) + .buildEntityEvent()); + break; //we should only have one entity per field anyways + } } } + case ExchangeInfinityStoneRequest, UseInfinityStoneRequest -> { + CharacterRequest data = (CharacterRequest)request; + result.add(new EventBuilder(request.type == RequestType.ExchangeInfinityStoneRequest ? EventType.ExchangeInfinityStoneEvent : EventType.UseInfinityStoneEvent) + .withOriginEntity(data.originEntity) + .withOriginField(data.originField) + .withTargetEntity(data.targetEntity) + .withTargetField(data.targetField) + .withStoneType(data.stoneType) + .buildCharacterEvent()); + result.add(new EventBuilder(EventType.ConsumedAPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(1) + .buildEntityEvent()); + + 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 -> { + EntityType target = data.originEntity.type == EntityType.P1 ? EntityType.P2 : EntityType.P1; + for(IntVector2 pos: rasterize(data.originField, data.targetField, false, true)) { + for(Entity entity: state.entities.findByPosition(pos)) { + if(entity.id.isSameType(target)) { + result.add(new EventBuilder(EventType.TakenDamageEvent) + .withTargetEntity(entity.id) + .withTargetField(pos) + .withAmount(data.value) + .buildEntityEvent()); + } + } + } + } + case RealityStone -> { + if(data.originEntity == data.targetEntity) { // => place stone + 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.getMax() * 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.getValue() - origin.ap.getMax(); + if(ap < 0) { + result.add(new EventBuilder(EventType.ConsumedAPEvent) + .withTargetEntity(data.originEntity) + .withTargetField(data.originField) + .withAmount(ap) + .buildEntityEvent()); + } + int mp = origin.mp.getValue() - origin.mp.getMax(); + 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.getMax()) + .buildEntityEvent()); + } + } + } + } + case EndRoundRequest -> { + result.addAll(handleTurnEnd(state)); //why is it called end round request when it ends a turn... + } + case Req -> { + result.add(buildGameStateEvent(state)); + } } - return options; + return result; } + /** * Applies an {@link Event} to a {@link GameState}. * @param state The game state to apply to * @param event The event to apply */ - public static void applyEvent(GameState state, Event event) { + protected static void applyEvent(GameState state, Event event) { switch(event.type) { case DestroyedEntityEvent -> { state.entities.removeEntity(((EntityEvent)event).targetEntity); @@ -596,6 +520,87 @@ class GameLogic { } } + + /** + * Builds a {@link EventType#GamestateEvent} for the given {@link GameState}. + * @param state The game state to use + * @return The resulting event + */ + public static Event buildGameStateEvent(GameState state) { + return new EventBuilder(EventType.GamestateEvent) + .withEntities(state.entities.export()) + .withTurnOrder(state.turnOrder.toArray(new EntityID[0])) + .withMapSize(state.mapSize) + .withActiveCharacter(state.activeCharacter) + .withStoneCooldowns(state.stoneCooldown.export()) + .withWinCondition(state.won) + .buildGameStateEvent(); + } + + + /** + * Checks if a line of sight exists between the two positions + * @param state The game state to work on + * @param start The first position + * @param end The second position + * @return Whether or not the light of sight exists + */ + public static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) { + for(IntVector2 pos: rasterize(start, end, false, false)) { + if(state.entities.blocksVision(pos)) { + return false; + } + } + return true; + } + + /** + * Finds free neighbour options from a starting field. + * @param state The game state to work on + * @param start The starting position + * @return A list of free neighbour field options + */ + public static ArrayList getFreeNeighbour(GameState state, IntVector2 start) { + ArrayList options = new ArrayList<>(); + + if(start.getX() < 0 || start.getX() >= state.mapSize.getX() || start.getY() < 0 || start.getY() >= state.mapSize.getY()) { + return options; + } + + for(IntVector2 dir: IntVector2.CardinalDirections) { + if(state.entities.findByPosition(start.add(dir)).size() == 0) { + options.add(start.add(dir)); + } + } + + if(options.size() == 0) { + return getFreeNeighbour(state, start.add(IntVector2.CardinalDirections[rand.nextInt(IntVector2.CardinalDirections.length)])); + }else { + return options; + } + } + + /** + * Finds free field options. + * @param state The game state to work on + * @return A list of free field options + */ + public static ArrayList getFreeFields(GameState state) { + ArrayList options = new ArrayList<>(); + + for(int x = 0; x < state.mapSize.getX(); x++) { + for(int y = 0; y < state.mapSize.getY(); y++) { + IntVector2 pos = new IntVector2(x, y); + if(state.entities.findByPosition(pos).size() == 0) { + options.add(pos); + } + } + } + + return options; + } + + /** * Starts the game and initializes all entities. * @param state The game state to work on @@ -603,7 +608,7 @@ class GameLogic { * @param selectedCharacters2 The characters selected by player 2 * @return The list of resulting {@link Event}s */ - public static ArrayList startGame(GameState state, ArrayList selectedCharacters1, ArrayList selectedCharacters2) { + protected static ArrayList startGame(GameState state, ArrayList selectedCharacters1, ArrayList selectedCharacters2) { ArrayList result = new ArrayList<>(); ArrayList free = new ArrayList<>(); @@ -652,12 +657,21 @@ class GameLogic { return result; } + /** + * Forcefully starts the next round. + * @param state The game state to work on + * @return The list of resulting {@link Event}s + */ + protected static ArrayList startRound(GameState state) { + return handleRoundStart(state); + } + /** * Starts end of round handling if necessary. * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList checkTurnEnd(GameState state) { + protected static ArrayList checkTurnEnd(GameState state) { if( ((Character) state.entities.findEntity(state.activeCharacter)).ap.getValue() <= 0 && ((Character) state.entities.findEntity(state.activeCharacter)).mp.getValue() <= 0 @@ -668,12 +682,22 @@ class GameLogic { return new ArrayList<>(); } + /** + * Forcefully ends the current turn. + * @param state The game state to work on + * @return The list of resulting {@link Event}s + */ + protected static ArrayList endTurn(GameState state) { + return handleTurnEnd(state); + } + + /** * Handles everything that happens at the end of a turn, including new rounds. * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleTurnEnd(GameState state) { + private static ArrayList handleTurnEnd(GameState state) { ArrayList result = new ArrayList<>(); ArrayList alive = new ArrayList<>(); @@ -721,7 +745,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleRoundStart(GameState state) { + private static ArrayList handleRoundStart(GameState state) { ArrayList result = new ArrayList<>(); state.roundNumber++; @@ -764,7 +788,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleGoose(GameState state) { + private static ArrayList handleGoose(GameState state) { ArrayList result = new ArrayList<>(); ArrayList inventory = new ArrayList<>(state.unvomitedStones); @@ -804,7 +828,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleStan(GameState state, HashSet revived) { + private static ArrayList handleStan(GameState state, HashSet revived) { ArrayList result = new ArrayList<>(); ArrayList characters = new ArrayList<>(); @@ -864,7 +888,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList spawnThanos(GameState state) { + private static ArrayList spawnThanos(GameState state) { ArrayList result = new ArrayList<>(); ArrayList free = getFreeFields(state); @@ -894,7 +918,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleThanos(GameState state, NPC thanos) { + private static ArrayList handleThanos(GameState state, NPC thanos) { ArrayList result = new ArrayList<>(); if(thanos.inventory.getFreeSlots() > 0) { @@ -1009,7 +1033,7 @@ class GameLogic { * @param state The game state to work on * @return The list of resulting {@link Event}s */ - public static ArrayList handleTurnStart(GameState state) { + private static ArrayList handleTurnStart(GameState state) { ArrayList result = new ArrayList<>(); state.turnNumber++; @@ -1068,7 +1092,7 @@ class GameLogic { * @param winner The winning character * @return The list of resulting {@link Event}s */ - public static ArrayList handlePlayerWin(GameState state, EntityType winner) { + private static ArrayList handlePlayerWin(GameState state, EntityType winner) { ArrayList result = new ArrayList<>(); state.won = true; @@ -1080,6 +1104,7 @@ class GameLogic { return result; } + /** * Computes all fields which intersect the line between the center points of the given two fields including the two defining points. * @return The list of intersecting positions diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java index 66d174e..aa70c3f 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java @@ -1,9 +1,9 @@ package uulm.teamname.marvelous.gamelibrary.gamelogic; import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.events.EventType; import uulm.teamname.marvelous.gamelibrary.requests.Request; -import java.util.ArrayDeque; import java.util.ArrayList; /** Represents manager for a game state. */ @@ -12,7 +12,7 @@ class GameStateManager { private final GameState state; /** The queue of {@link Event}s to be applied during {@link Request} processing */ - private final ArrayDeque queue = new ArrayDeque<>(); + private final ArrayList queue = new ArrayList<>(); /** * Constructs a new {@link GameStateManager}. @@ -26,13 +26,17 @@ class GameStateManager { * Checks a list of {@link Request}s for validity and optionally produces resulting {@link Event}s. * @param requests The requests to check * @param apply True if resulting events should be stored for later application + * @return Whether the requests are valid */ - public boolean processRequests(Request[] requests, boolean apply) { + public boolean processRequests(ArrayList requests, boolean apply) { GameState snapshot = state.snapshot(); for(Request request: requests) { if (GameLogic.checkRequest(snapshot, request)) { ArrayList result = GameLogic.executeRequest(snapshot, request); + for(Event event: result) { + GameLogic.applyEvent(snapshot, event); + } if(apply) { queue.addAll(result); } @@ -45,32 +49,6 @@ class GameStateManager { return true; } - /** - * Handles events that happen after a turn phase. - * @return The optionally resulting {@link Event}s - */ - public ArrayList checkPostPhase() { - return GameLogic.checkTurnEnd(state); - } - - /** - * Initializes the game. - * @param selectedCharacters1 The characters selected by player 1 - * @param selectedCharacters2 The characters selected by player 2 - * @return The resulting {@link Event}s - */ - public ArrayList initGame(ArrayList selectedCharacters1, ArrayList selectedCharacters2) { - return GameLogic.startGame(state, selectedCharacters1, selectedCharacters2); - } - - /** - * Starts the game. - * @return The resulting {@link Event}s - */ - public ArrayList startGame() { - return GameLogic.handleRoundStart(state); - } - /** * Applies an array of {@link Event}s to the game state. * @param events The events to apply @@ -81,17 +59,64 @@ class GameStateManager { } } + /** + * Applies an {@link Event} to the game state. + * @param event The event to apply + */ + public void applyEvent(Event event) { + GameLogic.applyEvent(state, event); + } + /** * Applies the result of the last processRequests call. * @return A list of applied events */ public ArrayList apply() { - ArrayList toReturn = new ArrayList<>(queue.size()); - Event current; - while ((current = queue.poll()) != null) { - GameLogic.applyEvent(state, current); - toReturn.add(current); + for(Event event: queue) { + GameLogic.applyEvent(state, event); } - return toReturn; + + // i feel so smart for remembering this trick + try { + return new ArrayList<>(queue); + }finally { + queue.clear(); + } + } + + /** + * Handles events that happen after a turn phase. + * @return The optionally resulting {@link Event}s + */ + public ArrayList checkPostPhase() { + ArrayList result = GameLogic.checkTurnEnd(state); + applyEvents(result); + return result; + } + + /** + * Initializes and starts the game. + * @param selectedCharacters1 The characters selected by player 1 + * @param selectedCharacters2 The characters selected by player 2 + * @return The resulting {@link Event}s + */ + public ArrayList startGame(ArrayList selectedCharacters1, ArrayList selectedCharacters2) { + ArrayList result = GameLogic.startGame(state, selectedCharacters1, selectedCharacters2); + applyEvents(result); + + ArrayList result2 = GameLogic.startRound(state); + applyEvents(result2); + + result.addAll(result2); + + return result; + } + + /** + * Produces a {@link EventType#GamestateEvent} for the current {@link GameState}. + * @return The resulting event + */ + public Event getGameStateEvent() { + return GameLogic.buildGameStateEvent(state); } } diff --git a/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogicTest.java b/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogicTest.java index 8e648ac..f097ff3 100644 --- a/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogicTest.java +++ b/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogicTest.java @@ -7,8 +7,6 @@ import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.config.*; import uulm.teamname.marvelous.gamelibrary.entities.*; import uulm.teamname.marvelous.gamelibrary.entities.Character; -import uulm.teamname.marvelous.gamelibrary.events.Event; -import uulm.teamname.marvelous.gamelibrary.events.EventType; import uulm.teamname.marvelous.gamelibrary.requests.*; import java.util.*; @@ -79,12 +77,12 @@ class GameLogicTest { props.name = generateName(10); - props.HP = Math.abs(randomIntegers.next() % 20); - props.MP = Math.abs(randomIntegers.next() % 7); - props.AP = Math.abs(randomIntegers.next() % 7); - props.meleeDamage = Math.abs(randomIntegers.next() % 7); - props.rangedDamage = Math.abs(randomIntegers.next() % 7); - props.attackRange = Math.abs(randomIntegers.next() % 7); + props.HP = Math.abs(randomIntegers.next() % 15 + 5); + props.MP = Math.abs(randomIntegers.next() % 5 + 2); + props.AP = Math.abs(randomIntegers.next() % 5 + 2); + props.meleeDamage = Math.abs(randomIntegers.next() % 5 + 2); + props.rangedDamage = Math.abs(randomIntegers.next() % 5 + 2); + props.attackRange = Math.abs(randomIntegers.next() % 5 + 2); return props; } @@ -161,35 +159,11 @@ class GameLogicTest { @Test void testGame() { - GameInstance game = new GameInstance(partyConfig, characterConfig, scenarioConfig); + GameInstance server = new GameInstance(partyConfig, characterConfig, scenarioConfig); - ArrayList result = game.startGame(player1Selection, player2Selection); + server.startGame(player1Selection, player2Selection); - assertTrue(result.size() > 0, "Start game should return at least one event"); - - Event actual = result.get(0); - - assertEquals(EventType.GamestateEvent, actual.type, "First event should be a GameStateEvent"); - - System.out.println(game.toString()); - - game.applyEvents(GameLogic.spawnThanos(game.getState())); - - System.out.println(game.toString()); - - NPC thanos = (NPC)game.state.getEntities().findEntity(new EntityID(EntityType.NPC, 2)); - - game.applyEvents(GameLogic.handleThanos(game.getState(), thanos)); - - System.out.println(game.toString()); - - game.applyEvents(GameLogic.handleThanos(game.getState(), thanos)); - - System.out.println(game.toString()); - - game.applyEvents(GameLogic.handleThanos(game.getState(), thanos)); - - System.out.println(game.toString()); + System.out.println(server.toString()); }