Merge branch 'gamelib' of https://gitlab.informatik.uni-ulm.de/sopra/ws20-marvelous-mashup/teams/team25 into gamelib
This commit is contained in:
		@ -16,6 +16,17 @@ public class IntVector2 implements Serializable {
 | 
			
		||||
    public final static IntVector2 Y = new IntVector2(0, 1);
 | 
			
		||||
    public final static IntVector2 Zero = new IntVector2(0, 0);
 | 
			
		||||
 | 
			
		||||
    public final static IntVector2[] CardinalDirections = {
 | 
			
		||||
            new IntVector2(1, 0),
 | 
			
		||||
            new IntVector2(0, 1),
 | 
			
		||||
            new IntVector2(1, 1),
 | 
			
		||||
            new IntVector2(-1, 0),
 | 
			
		||||
            new IntVector2(0, -1),
 | 
			
		||||
            new IntVector2(-1, -1),
 | 
			
		||||
            new IntVector2(1, -1),
 | 
			
		||||
            new IntVector2(-1, 1),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private final static float nearZero = 0.000000001f;
 | 
			
		||||
 | 
			
		||||
    public IntVector2(int x, int y) {
 | 
			
		||||
 | 
			
		||||
@ -11,5 +11,7 @@ public enum EntityType {
 | 
			
		||||
    /** Represents a Rock entity */
 | 
			
		||||
    Rocks,
 | 
			
		||||
    /** Represents an InfinityStone entity */
 | 
			
		||||
    InfinityStones
 | 
			
		||||
    InfinityStones,
 | 
			
		||||
    /** Represents nothing */
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,11 @@ public class Inventory implements Iterable<StoneType> {
 | 
			
		||||
        return size - content.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Returns the number stones the inventory has. */
 | 
			
		||||
    public int getSize() {
 | 
			
		||||
        return content.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the inventory contains the given stone.
 | 
			
		||||
     * @param stone The {@link StoneType} to check for
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ import uulm.teamname.marvelous.gamelibrary.events.Event;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.events.EventType;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Observer;
 | 
			
		||||
 | 
			
		||||
/** Represents a game instance. */
 | 
			
		||||
@ -101,6 +100,7 @@ public class GameInstance {
 | 
			
		||||
     * @param events The events to emit
 | 
			
		||||
     */
 | 
			
		||||
    private void emit(Event... events) {
 | 
			
		||||
        manager.applyEvents(events);
 | 
			
		||||
        emitter.update(events);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
 | 
			
		||||
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.IntVector2;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.entities.*;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.entities.Character;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.events.*;
 | 
			
		||||
@ -12,10 +11,12 @@ import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.geom.Line2D;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
/** Contains game logic handling. */
 | 
			
		||||
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
 | 
			
		||||
@ -173,6 +174,9 @@ class GameLogic {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            case EndRoundRequest -> {
 | 
			
		||||
                result.addAll(handleTurnEnd(state)); //why is it called end round request when it ends a turn...
 | 
			
		||||
            }
 | 
			
		||||
            case Req -> {
 | 
			
		||||
                result.add(buildGameStateEvent(state));
 | 
			
		||||
            }
 | 
			
		||||
@ -283,6 +287,10 @@ class GameLogic {
 | 
			
		||||
                    requireAP(origin, 1);
 | 
			
		||||
                    requireInfinityStone(origin, data.stoneType);
 | 
			
		||||
 | 
			
		||||
                    if(state.stoneCooldown.onCooldown(data.stoneType)) {
 | 
			
		||||
                        throw new InvalidRequestException();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    switch(((CharacterRequest) request).stoneType) {
 | 
			
		||||
                        case SpaceStone -> {
 | 
			
		||||
                            verifyCoordinates(state, data.targetField);
 | 
			
		||||
@ -338,7 +346,7 @@ class GameLogic {
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                case Req -> {
 | 
			
		||||
                case EndRoundRequest, Req -> {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -418,20 +426,60 @@ class GameLogic {
 | 
			
		||||
     * Verifies that there is a line of sight between two positions.
 | 
			
		||||
     */
 | 
			
		||||
    private static void requireLineOfSight(GameState state, IntVector2 start, IntVector2 end) throws InvalidRequestException {
 | 
			
		||||
        if(!checkLineOfSight(state, start, end)) {
 | 
			
		||||
            throw new InvalidRequestException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
     */
 | 
			
		||||
    private static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) {
 | 
			
		||||
        //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
 | 
			
		||||
        //TODO: implement proper line rasterization algorithm in GameLogic.checkLineOfSight
 | 
			
		||||
        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();
 | 
			
		||||
                        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
 | 
			
		||||
     */
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -445,7 +493,14 @@ class GameLogic {
 | 
			
		||||
                state.entities.removeEntity(((EntityEvent)event).targetEntity);
 | 
			
		||||
            }
 | 
			
		||||
            case TakenDamageEvent -> {
 | 
			
		||||
                ((Character)state.entities.findEntity(((CharacterEvent)event).targetEntity)).hp.decreaseValue(((CharacterEvent)event).amount);
 | 
			
		||||
                Character target = (Character)state.entities.findEntity(((CharacterEvent)event).targetEntity);
 | 
			
		||||
                target.hp.decreaseValue(((CharacterEvent)event).amount);
 | 
			
		||||
 | 
			
		||||
                EntityType opposing = target.id.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
 | 
			
		||||
                state.winConditions.increaseValue(opposing, WinCondition.TotalDamage, ((CharacterEvent)event).amount);
 | 
			
		||||
                if(target.hp.getValue() == 0) {
 | 
			
		||||
                    state.winConditions.increaseValue(opposing, WinCondition.TotalKnockouts, 1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            case ConsumedAPEvent -> {
 | 
			
		||||
                ((Character)state.entities.findEntity(((CharacterEvent)event).targetEntity)).ap.decreaseValue(((CharacterEvent)event).amount);
 | 
			
		||||
@ -460,11 +515,25 @@ class GameLogic {
 | 
			
		||||
                ((Character)state.entities.findEntity(((CharacterEvent)event).targetEntity)).hp.increaseValue(((CharacterEvent)event).amount);
 | 
			
		||||
            }
 | 
			
		||||
            case MoveEvent -> {
 | 
			
		||||
                (state.entities.findEntity(((CharacterEvent)event).originEntity)).setPosition(((CharacterEvent)event).targetField);
 | 
			
		||||
                Character target = (Character)state.entities.findEntity(((CharacterEvent)event).originEntity);
 | 
			
		||||
                for(Entity entity: state.entities.findByPosition(((CharacterEvent)event).targetField)) {
 | 
			
		||||
                    if(entity instanceof InfinityStone) {
 | 
			
		||||
                        target.inventory.addStone(((InfinityStone)entity).type);
 | 
			
		||||
 | 
			
		||||
                        state.winConditions.updateValue(target.id.type, WinCondition.MaxStones, target.inventory.getSize());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                target.setPosition(((CharacterEvent)event).targetField);
 | 
			
		||||
            }
 | 
			
		||||
            case UseInfinityStoneEvent -> {
 | 
			
		||||
                state.stoneCooldown.setCooldown(((CharacterEvent)event).stoneType, 10); //TODO: use stone cooldown from config
 | 
			
		||||
            }
 | 
			
		||||
            case ExchangeInfinityStoneEvent -> {
 | 
			
		||||
                ((Character)state.entities.findEntity(((CharacterEvent)event).originEntity)).inventory.removeStone(((CharacterEvent)event).stoneType);
 | 
			
		||||
                ((Character)state.entities.findEntity(((CharacterEvent)event).targetEntity)).inventory.addStone(((CharacterEvent)event).stoneType);
 | 
			
		||||
                Character target = (Character)state.entities.findEntity(((CharacterEvent)event).targetEntity);
 | 
			
		||||
                target.inventory.addStone(((CharacterEvent)event).stoneType);
 | 
			
		||||
 | 
			
		||||
                state.winConditions.updateValue(target.id.type, WinCondition.MaxStones, target.inventory.getSize());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -493,13 +562,9 @@ class GameLogic {
 | 
			
		||||
    public static ArrayList<Event> handleTurnEnd(GameState state) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        ArrayList<EntityID> order = state.turnOrder;
 | 
			
		||||
        ArrayList<EntityID> alive = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        state.turnNumber++;
 | 
			
		||||
        boolean newRound = false;
 | 
			
		||||
 | 
			
		||||
        for (EntityID id: order) {
 | 
			
		||||
        for (EntityID id: state.turnOrder) {
 | 
			
		||||
            Character character = ((Character)state.entities.findEntity(id));
 | 
			
		||||
 | 
			
		||||
            if(character.hp.getValue() > 0){
 | 
			
		||||
@ -507,93 +572,219 @@ class GameLogic {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(character.inventory.getFreeSlots() == 0) { // no slots => has all infinity stones
 | 
			
		||||
                state.won = true;
 | 
			
		||||
                result.add(new EventBuilder(EventType.WinEvent)
 | 
			
		||||
                        .withPlayerWon(character.id.id)
 | 
			
		||||
                        .buildGameEvent());
 | 
			
		||||
                result.addAll(handlePlayerWin(state, character.id.type));
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(alive.isEmpty()) {
 | 
			
		||||
            EntityType winner = state.winConditions.getWinner();
 | 
			
		||||
            if(winner == EntityType.None) {
 | 
			
		||||
                winner = rand.nextBoolean() ? EntityType.P1 : EntityType.P2;
 | 
			
		||||
            }
 | 
			
		||||
            result.addAll(handlePlayerWin(state, winner));
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            //thanos win handling
 | 
			
		||||
 | 
			
		||||
        int index = alive.indexOf(state.activeCharacter);
 | 
			
		||||
        if(index == alive.size() - 1) {
 | 
			
		||||
            result.addAll(handleRoundStart(state));
 | 
			
		||||
        }else {
 | 
			
		||||
 | 
			
		||||
            int activeIndex = state.activeCharacter != null ? alive.indexOf(state.activeCharacter) : -1;
 | 
			
		||||
            if(activeIndex == -1 || activeIndex == alive.size() - 1) {
 | 
			
		||||
                state.activeCharacter = alive.get(0);
 | 
			
		||||
                //reached end of turn order, new round
 | 
			
		||||
                state.roundNumber++;
 | 
			
		||||
                newRound = true;
 | 
			
		||||
            }else {
 | 
			
		||||
                state.activeCharacter = alive.get(activeIndex + 1);
 | 
			
		||||
            }
 | 
			
		||||
            Character activeCharacter = (Character)state.entities.findEntity(state.activeCharacter);
 | 
			
		||||
 | 
			
		||||
            result.add(new EventBuilder(EventType.ConsumedAPEvent)
 | 
			
		||||
                    .withTargetEntity(state.activeCharacter)
 | 
			
		||||
                    .withTargetField(activeCharacter.getPosition())
 | 
			
		||||
                    .withAmount(activeCharacter.ap.getValue() - activeCharacter.ap.max)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
            result.add(new EventBuilder(EventType.ConsumedMPEvent)
 | 
			
		||||
                    .withTargetEntity(state.activeCharacter)
 | 
			
		||||
                    .withTargetField(activeCharacter.getPosition())
 | 
			
		||||
                    .withAmount(activeCharacter.mp.getValue() - activeCharacter.mp.max)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
            result.add(new EventBuilder(EventType.TurnEvent)
 | 
			
		||||
                    .withTurnCount(alive.size())
 | 
			
		||||
                    .withNextCharacter(state.activeCharacter)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
 | 
			
		||||
            state.activeCharacter = alive.get(index + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(newRound) {
 | 
			
		||||
            //special round handling
 | 
			
		||||
            //shuffle turn order
 | 
			
		||||
            /*
 | 
			
		||||
            result.add(new EventBuilder(EventType.RoundSetupEvent)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
        result.addAll(handleTurnStart(state));
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks a {@link GameState} for the current overtime win condition.
 | 
			
		||||
     * @param state The game state to check
 | 
			
		||||
     * @return The {@link ParticipantType} that is currently winning the game according to overtime ruling
 | 
			
		||||
     * Handles everything that happens at the beginning of new rounds.
 | 
			
		||||
     * @param state The game state to work on
 | 
			
		||||
     * @return The list of resulting {@link Event}s
 | 
			
		||||
     */
 | 
			
		||||
    public static ParticipantType checkWinConditions(GameState state) {
 | 
			
		||||
        //TODO: GameLogic.checkWinConditions is kind of ugly
 | 
			
		||||
    public static ArrayList<Event> handleRoundStart(GameState state) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        Tuple<ParticipantType, WinCondition> player1;
 | 
			
		||||
        Tuple<ParticipantType, WinCondition> player2;
 | 
			
		||||
        int value1;
 | 
			
		||||
        int value2;
 | 
			
		||||
        for(WinCondition condition: WinCondition.values()) {
 | 
			
		||||
            player1 = new Tuple<ParticipantType, WinCondition>(ParticipantType.Player1, condition);
 | 
			
		||||
            player2 = new Tuple<ParticipantType, WinCondition>(ParticipantType.Player2, condition);
 | 
			
		||||
            value1 = 0;
 | 
			
		||||
            value2 = 0;
 | 
			
		||||
        state.roundNumber++;
 | 
			
		||||
 | 
			
		||||
            if(state.winConditions.containsKey(player1)) {
 | 
			
		||||
                value1 = state.winConditions.get(player1);
 | 
			
		||||
            }
 | 
			
		||||
            if(state.winConditions.containsKey(player2)) {
 | 
			
		||||
                value2 = state.winConditions.get(player2);
 | 
			
		||||
        if(state.roundNumber >= 1 && state.roundNumber <= 6) {
 | 
			
		||||
            result.addAll(handleGoose(state));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if(value1 > value2) {
 | 
			
		||||
                return ParticipantType.Player1;
 | 
			
		||||
        HashSet<EntityID> revived = new HashSet<>();
 | 
			
		||||
        if(state.roundNumber == 7) {
 | 
			
		||||
            result.addAll(handleStan(state, revived));
 | 
			
		||||
        }
 | 
			
		||||
            if(value2 > value1) {
 | 
			
		||||
                return ParticipantType.Player2;
 | 
			
		||||
 | 
			
		||||
        //TODO: add handling for thanos
 | 
			
		||||
 | 
			
		||||
        Collections.shuffle(state.turnOrder);
 | 
			
		||||
 | 
			
		||||
        for (EntityID id: state.turnOrder) {
 | 
			
		||||
            if(revived.contains(id) || ((Character)state.entities.findEntity(id)).hp.getValue() > 0){
 | 
			
		||||
                state.activeCharacter = id;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ParticipantType.None;
 | 
			
		||||
        state.stoneCooldown.update();
 | 
			
		||||
 | 
			
		||||
        result.add(new EventBuilder(EventType.RoundSetupEvent)
 | 
			
		||||
                .withRoundCount(state.roundNumber)
 | 
			
		||||
                .withCharacterOrder(state.turnOrder.toArray(new EntityID[0]))
 | 
			
		||||
                .buildGameEvent());
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles the actions of Goose at rounds 1-6.
 | 
			
		||||
     * @param state The game state to work on
 | 
			
		||||
     * @return The list of resulting {@link Event}s
 | 
			
		||||
     */
 | 
			
		||||
    public static ArrayList<Event> handleGoose(GameState state) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        StoneType[] available = state.unvomitedStones.toArray(new StoneType[0]);
 | 
			
		||||
        StoneType stone = available[rand.nextInt(available.length)];
 | 
			
		||||
        state.unvomitedStones.remove(stone);
 | 
			
		||||
 | 
			
		||||
        ArrayList<IntVector2> free = 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) {
 | 
			
		||||
                    free.add(pos);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        IntVector2 position = free.get(rand.nextInt(free.size()));
 | 
			
		||||
 | 
			
		||||
        EntityID goose = new EntityID(EntityType.NPC, 0);
 | 
			
		||||
        result.add(new EventBuilder(EventType.SpawnEntityEvent)
 | 
			
		||||
                .withEntity(new NPC(goose, position))
 | 
			
		||||
                .buildEntityEvent());
 | 
			
		||||
        result.add(new EventBuilder(EventType.SpawnEntityEvent)
 | 
			
		||||
                .withEntity(new InfinityStone(new EntityID(EntityType.InfinityStones, stone.getID()), position, stone))
 | 
			
		||||
                .buildEntityEvent());
 | 
			
		||||
        result.add(new EventBuilder(EventType.DestroyedEntityEvent)
 | 
			
		||||
                .withTargetEntity(goose)
 | 
			
		||||
                .withTargetField(position)
 | 
			
		||||
                .buildEntityEvent());
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles the actions of Stan at round 7.
 | 
			
		||||
     * @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) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        ArrayList<Character> characters = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        ArrayList<IntVector2> targetOptions = new ArrayList<>();
 | 
			
		||||
        int lowest = -1;
 | 
			
		||||
        for(EntityID id: state.turnOrder) {
 | 
			
		||||
            Character character = (Character)state.entities.findEntity(id);
 | 
			
		||||
            characters.add(character);
 | 
			
		||||
 | 
			
		||||
            if(lowest == -1 || character.hp.getValue() < lowest) {
 | 
			
		||||
                lowest = character.hp.getValue();
 | 
			
		||||
                targetOptions.clear();
 | 
			
		||||
            }
 | 
			
		||||
            if(lowest == character.hp.getValue()) {
 | 
			
		||||
                targetOptions.add(character.getPosition());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        IntVector2 targetPosition = targetOptions.get(rand.nextInt(targetOptions.size()));
 | 
			
		||||
 | 
			
		||||
        ArrayList<IntVector2> spawnOptions = getFreeNeighbour(state, targetPosition);
 | 
			
		||||
        if(spawnOptions.size() == 0) {
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        IntVector2 spawnPosition = spawnOptions.get(rand.nextInt(spawnOptions.size()));
 | 
			
		||||
 | 
			
		||||
        EntityID stan = new EntityID(EntityType.NPC, 1);
 | 
			
		||||
        result.add(new EventBuilder(EventType.SpawnEntityEvent)
 | 
			
		||||
                .withEntity(new NPC(stan, spawnPosition))
 | 
			
		||||
                .buildEntityEvent());
 | 
			
		||||
 | 
			
		||||
        for(Character character: characters) {
 | 
			
		||||
            if(checkLineOfSight(state, spawnPosition, character.getPosition())) {
 | 
			
		||||
                if(character.hp.getValue() == 0) {
 | 
			
		||||
                    revived.add(character.id);
 | 
			
		||||
                }
 | 
			
		||||
                if(character.hp.getValue() != character.hp.max) {
 | 
			
		||||
                    result.add(new EventBuilder(EventType.HealedEvent)
 | 
			
		||||
                            .withTargetEntity(character.id)
 | 
			
		||||
                            .withTargetField(character.getPosition())
 | 
			
		||||
                            .withAmount(character.hp.max - character.hp.getValue())
 | 
			
		||||
                            .buildEntityEvent());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        result.add(new EventBuilder(EventType.DestroyedEntityEvent)
 | 
			
		||||
                .withTargetEntity(stan)
 | 
			
		||||
                .withTargetField(spawnPosition)
 | 
			
		||||
                .buildEntityEvent());
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles everything that happens at the beginning of a turn.
 | 
			
		||||
     * @param state The game state to work on
 | 
			
		||||
     * @return The list of resulting {@link Event}s
 | 
			
		||||
     */
 | 
			
		||||
    public static ArrayList<Event> handleTurnStart(GameState state) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        state.turnNumber++;
 | 
			
		||||
 | 
			
		||||
        Character activeCharacter = (Character)state.entities.findEntity(state.activeCharacter);
 | 
			
		||||
 | 
			
		||||
        if(activeCharacter.ap.getValue() != activeCharacter.ap.max) {
 | 
			
		||||
            result.add(new EventBuilder(EventType.ConsumedAPEvent)
 | 
			
		||||
                    .withTargetEntity(state.activeCharacter)
 | 
			
		||||
                    .withTargetField(activeCharacter.getPosition())
 | 
			
		||||
                    .withAmount(activeCharacter.ap.getValue() - activeCharacter.ap.max)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
        }
 | 
			
		||||
        if(activeCharacter.mp.getValue() != activeCharacter.mp.max) {
 | 
			
		||||
            result.add(new EventBuilder(EventType.ConsumedMPEvent)
 | 
			
		||||
                    .withTargetEntity(state.activeCharacter)
 | 
			
		||||
                    .withTargetField(activeCharacter.getPosition())
 | 
			
		||||
                    .withAmount(activeCharacter.mp.getValue() - activeCharacter.mp.max)
 | 
			
		||||
                    .buildGameEvent());
 | 
			
		||||
        }
 | 
			
		||||
        result.add(new EventBuilder(EventType.TurnEvent)
 | 
			
		||||
                .withTurnCount(state.turnOrder.size())
 | 
			
		||||
                .withNextCharacter(state.activeCharacter)
 | 
			
		||||
                .buildGameEvent());
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles the victory of a player through one character.
 | 
			
		||||
     * @param state The game state to work on
 | 
			
		||||
     * @param winner The winning character
 | 
			
		||||
     * @return The list of resulting {@link Event}s
 | 
			
		||||
     */
 | 
			
		||||
    public static ArrayList<Event> handlePlayerWin(GameState state, EntityType winner) {
 | 
			
		||||
        ArrayList<Event> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        state.won = true;
 | 
			
		||||
 | 
			
		||||
        result.add(new EventBuilder(EventType.WinEvent)
 | 
			
		||||
                .withPlayerWon(winner == EntityType.P1 ? 1 : 2)
 | 
			
		||||
                .buildGameEvent());
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,10 @@ package uulm.teamname.marvelous.gamelibrary.gamelogic;
 | 
			
		||||
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.IntVector2;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.entities.*;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
/** Represents the state of a game instance. */
 | 
			
		||||
class GameState {
 | 
			
		||||
@ -15,6 +15,9 @@ class GameState {
 | 
			
		||||
    /** The list of {@link Entity}s inside the game */
 | 
			
		||||
    public final EntityManager entities = new EntityManager();
 | 
			
		||||
 | 
			
		||||
    /** The set of stones that are yet to be placed on the map */
 | 
			
		||||
    public HashSet<StoneType> unvomitedStones = new HashSet<>(Arrays.asList(StoneType.values()));
 | 
			
		||||
 | 
			
		||||
    /** The total amount of full turn cycles that occurred */
 | 
			
		||||
    public int roundNumber = 0;
 | 
			
		||||
 | 
			
		||||
@ -33,8 +36,8 @@ class GameState {
 | 
			
		||||
    /** The global cooldown of every infinity stone */
 | 
			
		||||
    public final StoneCooldownManager stoneCooldown = new StoneCooldownManager();
 | 
			
		||||
 | 
			
		||||
    /** The store of the {@link WinCondition} data for every win condition for each player */
 | 
			
		||||
    public final HashMap<Tuple<ParticipantType, WinCondition>, Integer> winConditions = new HashMap<>();
 | 
			
		||||
    /** The data for every win condition for each player */
 | 
			
		||||
    public final WinConditionManager winConditions = new WinConditionManager();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new {@link GameState}.
 | 
			
		||||
@ -68,9 +71,7 @@ class GameState {
 | 
			
		||||
 | 
			
		||||
        clone.stoneCooldown.cloneFrom(stoneCooldown);
 | 
			
		||||
 | 
			
		||||
        for(Tuple<ParticipantType, WinCondition> condition: winConditions.keySet()) {
 | 
			
		||||
            clone.winConditions.put(condition, winConditions.get(condition));
 | 
			
		||||
        }
 | 
			
		||||
        clone.winConditions.cloneFrom(winConditions);
 | 
			
		||||
 | 
			
		||||
        return clone;
 | 
			
		||||
    }
 | 
			
		||||
@ -99,8 +100,6 @@ class GameState {
 | 
			
		||||
 | 
			
		||||
        stoneCooldown.cloneFrom(state.stoneCooldown);
 | 
			
		||||
 | 
			
		||||
        for(Tuple<ParticipantType, WinCondition> condition: state.winConditions.keySet()) {
 | 
			
		||||
            winConditions.put(condition, state.winConditions.get(condition));
 | 
			
		||||
        }
 | 
			
		||||
        winConditions.cloneFrom(state.winConditions);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
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.StoneType;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
 | 
			
		||||
/** Represents a game state view containing getters for all the properties of a {@link GameState}. */
 | 
			
		||||
public class GameStateView {
 | 
			
		||||
@ -53,7 +52,7 @@ public class GameStateView {
 | 
			
		||||
        return state.stoneCooldown.getCooldown(stone);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ParticipantType getWinnerAccordingToWinConditions() {
 | 
			
		||||
        return GameLogic.checkWinConditions(state);
 | 
			
		||||
    public EntityType getCurrentOvertimeWinner() {
 | 
			
		||||
        return state.winConditions.getWinner();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,76 @@
 | 
			
		||||
package uulm.teamname.marvelous.gamelibrary.gamelogic;
 | 
			
		||||
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
 | 
			
		||||
/** Represents a manager for win conditions. */
 | 
			
		||||
public class WinConditionManager {
 | 
			
		||||
    /** The store of the {@link WinCondition} data for every win condition for each player */
 | 
			
		||||
    private final HashMap<Tuple<EntityType, WinCondition>, Integer> winConditions = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the current winner
 | 
			
		||||
     * @return The {@link EntityType} that is currently winning the game according to overtime ruling
 | 
			
		||||
     */
 | 
			
		||||
    public EntityType getWinner() {
 | 
			
		||||
        int value1;
 | 
			
		||||
        int value2;
 | 
			
		||||
        for(WinCondition condition: WinCondition.values()) {
 | 
			
		||||
            value1 = getValue(EntityType.P1, condition);
 | 
			
		||||
            value2 = getValue(EntityType.P2, condition);
 | 
			
		||||
 | 
			
		||||
            if(value1 > value2) {
 | 
			
		||||
                return EntityType.P1;
 | 
			
		||||
            }
 | 
			
		||||
            if(value2 > value1) {
 | 
			
		||||
                return EntityType.P2;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return EntityType.None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the value for a win condition for a specific player if the new value is greater than the old one.
 | 
			
		||||
     * @param player The player to use
 | 
			
		||||
     * @param condition The {@link WinCondition} to use
 | 
			
		||||
     * @param value The new value
 | 
			
		||||
     */
 | 
			
		||||
    public void updateValue(EntityType player, WinCondition condition, Integer value) {
 | 
			
		||||
        Integer old = getValue(player, condition);
 | 
			
		||||
        if(old < value) {
 | 
			
		||||
            winConditions.put(new Tuple<>(player, condition), value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Increases the value for a win condition for a specific player.
 | 
			
		||||
     * @param player The player to use
 | 
			
		||||
     * @param condition The {@link WinCondition} to use
 | 
			
		||||
     * @param value The new value
 | 
			
		||||
     */
 | 
			
		||||
    public void increaseValue(EntityType player, WinCondition condition, Integer value) {
 | 
			
		||||
        winConditions.put(new Tuple<>(player, condition), getValue(player, condition) + value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the value for a specific player and win condition.
 | 
			
		||||
     * @param player The player to use
 | 
			
		||||
     * @param condition The {@link WinCondition} to use
 | 
			
		||||
     * @return The value of the condition or 0 if it doesn't exist
 | 
			
		||||
     */
 | 
			
		||||
    private Integer getValue(EntityType player, WinCondition condition) {
 | 
			
		||||
        return winConditions.getOrDefault(new Tuple<>(player, condition), 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Takes over all the win conditions from a different {@link WinConditionManager}.
 | 
			
		||||
     * @param other The win condition manager to take the data from
 | 
			
		||||
     */
 | 
			
		||||
    public void cloneFrom(WinConditionManager other) {
 | 
			
		||||
        winConditions.clear();
 | 
			
		||||
        winConditions.putAll(other.winConditions);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user