refactor: restructure code and fix some events not being applied

This commit is contained in:
punchready 2021-06-03 02:19:10 +02:00
parent e0af4cf01c
commit 3bb6d81b27
4 changed files with 371 additions and 364 deletions

View File

@ -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<List<Event>> checkRequestsAndApply(Request... requests) {
public Optional<List<Event>> checkRequestsAndApply(ArrayList<Request> requests) {
if(manager.processRequests(requests, true)) {
ArrayList<Event> result = manager.apply();
ArrayList<Event> 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<Request> 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<Event> startGame(ArrayList<Integer> selectedCharacters1, ArrayList<Integer> selectedCharacters2) {
ArrayList<Event> result = manager.initGame(selectedCharacters1, selectedCharacters2);
manager.applyEvents(result);
ArrayList<Event> 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<Event> startGame(ArrayList<Integer> selectedCharacters1, ArrayList<Integer> 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

View File

@ -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<Event> executeRequest(GameState state, Request request) {
ArrayList<Event> 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<Event> executeRequest(GameState state, Request request) {
ArrayList<Event> 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<IntVector2> getFreeNeighbour(GameState state, IntVector2 start) {
ArrayList<IntVector2> 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<IntVector2> getFreeFields(GameState state) {
ArrayList<IntVector2> 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<IntVector2> getFreeNeighbour(GameState state, IntVector2 start) {
ArrayList<IntVector2> 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<IntVector2> getFreeFields(GameState state) {
ArrayList<IntVector2> 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<Event> startGame(GameState state, ArrayList<Integer> selectedCharacters1, ArrayList<Integer> selectedCharacters2) {
protected static ArrayList<Event> startGame(GameState state, ArrayList<Integer> selectedCharacters1, ArrayList<Integer> selectedCharacters2) {
ArrayList<Event> result = new ArrayList<>();
ArrayList<IntVector2> 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<Event> 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<Event> checkTurnEnd(GameState state) {
protected static ArrayList<Event> 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<Event> 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<Event> handleTurnEnd(GameState state) {
private static ArrayList<Event> handleTurnEnd(GameState state) {
ArrayList<Event> result = new ArrayList<>();
ArrayList<EntityID> 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<Event> handleRoundStart(GameState state) {
private static ArrayList<Event> handleRoundStart(GameState state) {
ArrayList<Event> 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<Event> handleGoose(GameState state) {
private static ArrayList<Event> handleGoose(GameState state) {
ArrayList<Event> result = new ArrayList<>();
ArrayList<StoneType> 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<Event> handleStan(GameState state, HashSet<EntityID> revived) {
private static ArrayList<Event> handleStan(GameState state, HashSet<EntityID> revived) {
ArrayList<Event> result = new ArrayList<>();
ArrayList<Character> 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<Event> spawnThanos(GameState state) {
private static ArrayList<Event> spawnThanos(GameState state) {
ArrayList<Event> result = new ArrayList<>();
ArrayList<IntVector2> 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<Event> handleThanos(GameState state, NPC thanos) {
private static ArrayList<Event> handleThanos(GameState state, NPC thanos) {
ArrayList<Event> 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<Event> handleTurnStart(GameState state) {
private static ArrayList<Event> handleTurnStart(GameState state) {
ArrayList<Event> 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<Event> handlePlayerWin(GameState state, EntityType winner) {
private static ArrayList<Event> handlePlayerWin(GameState state, EntityType winner) {
ArrayList<Event> 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

View File

@ -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<Event> queue = new ArrayDeque<>();
private final ArrayList<Event> 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<Request> requests, boolean apply) {
GameState snapshot = state.snapshot();
for(Request request: requests) {
if (GameLogic.checkRequest(snapshot, request)) {
ArrayList<Event> 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<Event> 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<Event> initGame(ArrayList<Integer> selectedCharacters1, ArrayList<Integer> selectedCharacters2) {
return GameLogic.startGame(state, selectedCharacters1, selectedCharacters2);
}
/**
* Starts the game.
* @return The resulting {@link Event}s
*/
public ArrayList<Event> 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<Event> apply() {
ArrayList<Event> 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<Event> checkPostPhase() {
ArrayList<Event> 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<Event> startGame(ArrayList<Integer> selectedCharacters1, ArrayList<Integer> selectedCharacters2) {
ArrayList<Event> result = GameLogic.startGame(state, selectedCharacters1, selectedCharacters2);
applyEvents(result);
ArrayList<Event> 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);
}
}

View File

@ -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<Event> 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());
}