mourning-ember / src / main / java / dev / llan / model / state / combat / CombatManager.java
CombatManager.java
Raw
package dev.llan.model.state.combat;

import dev.llan.events.*;
import dev.llan.model.state.combat.actions.CardUse;
import dev.llan.model.state.combat.actions.Move;
import dev.llan.model.state.combat.actions.Wait;
import dev.llan.events.Events.*;
import dev.llan.model.Game;
import dev.llan.model.board.GameMap;
import dev.llan.model.pieces.Entity;
import dev.llan.model.pieces.enemies.Enemy;
import dev.llan.model.pieces.player.Player;
import dev.llan.model.state.State;
import dev.llan.model.state.combat.actions.Action;

import java.util.*;

public class CombatManager {
  public enum CombatPhase {
    TURN,
    WAIT,
    NONE
  }

  private static final Comparator<Entity> turnComparator =
      (o1, o2) -> Double.compare(o2.getStats().getSpeed(), o1.getStats().getSpeed());
  private static final Comparator<Entity> waitComparator =
      Comparator.comparingDouble(o -> o.getStats().getSpeed());

  private final Game game;
  private final List<Entity> entities;
  private final Player player;
  private final GameMap map;

  private final PriorityQueue<Entity> turnQueue;
  private final PriorityQueue<Entity> waitQueue;

  private Entity currentTurn;
  private CombatPhase phase;

  public CombatManager(Player player, List<Enemy> enemies, GameMap map, Game game) {
    this.game = game;
    this.player = player;

    entities = new ArrayList<>();
    entities.add(player);
    addCombatEnemies(enemies);

    this.map = map;
    turnQueue = new PriorityQueue<>(entities.size(), turnComparator);
    waitQueue = new PriorityQueue<>(entities.size(), waitComparator);

    phase = CombatPhase.NONE;
  }

  private void addCombatEnemies(List<Enemy> enemies) {
    Set<Enemy> notified = new HashSet<>();
    for (Enemy enemy : enemies) {
      if (enemy.inCombat()) {
        notified.add(enemy);
        enemy.notifyNearbyEnemies(enemies, notified);
      }
    }

    this.addAllEntities(notified);
  }

  public void addAllEntities(Set<Enemy> entities) {
    for (Enemy enemy : entities) {
      if (!enemy.inCombat()) {
        enemy.setInCombat();
      }
    }
    this.entities.addAll(entities);
  }

  public List<GameEvent> initCombat() {
    List<GameEvent> events = new ArrayList<>();
    events.addAll(startRound());
    events.addAll(nextTurn());
    events.addAll(processUntilPlayer());
    return events;
  }

  private List<GameEvent> startRound() {
    List<GameEvent> events = new ArrayList<>();
    for (Entity entity : entities) {
      entity.onNextCombatRound();
    }
    events.add(new PlayerUpdatedEvent(PlayerUpdateType.ATTRIBUTES));
    events.addAll(checkEliminations());
    turnQueue.addAll(entities);
    return events;
  }

  public List<GameEvent> nextTurn() {
    List<GameEvent> events = new ArrayList<>();
    if (turnQueue.isEmpty() && waitQueue.isEmpty() && entities.size() > 1) {
      events.addAll(startRound());
      nextTurn();
    } else if (turnQueue.isEmpty()) {
      currentTurn = waitQueue.poll();
      phase = CombatPhase.WAIT;
    } else {
      currentTurn = turnQueue.poll();
      phase = CombatPhase.TURN;
    }
    if(currentTurn instanceof Player) {
      events.add(new PlayerUpdatedEvent(PlayerUpdateType.ATTRIBUTES));
    }
    return events;
  }

  public List<GameEvent> processUntilPlayer() {
    List<GameEvent> events = new ArrayList<>();
    while (currentTurn instanceof Enemy) {
      events.addAll(processEnemyTurn());
      events.addAll(nextTurn());
    }
    return events;
  }

  private List<GameEvent> processEnemyTurn() {
    List<Action> actions = ((Enemy) currentTurn).generateActions(map, player, phase);
    return processActions(actions);
  }

  private List<GameEvent> processActions(List<Action> actions) {
    List<GameEvent> events = new ArrayList<>();
    for (Action action : actions) {
      switch (action.getActionType()) {
        case MOVE -> events.addAll(processMove((Move) action));
        case WAIT -> events.addAll(processWait((Wait) action));
        case CARD_USE -> events.addAll(processCardUse((CardUse) action));
      }
    }

    return events;
  }

  public List<GameEvent> processAction(Action action) {
    return switch (action.getActionType()) {
      case MOVE -> processMove((Move) action);
      case WAIT -> processWait((Wait) action);
      case CARD_USE -> processCardUse((CardUse) action);
    };
  }

  private List<GameEvent> processMove(Move move) {
    return map.moveEntity(move.getActor(), move.getPath());
  }

  private List<GameEvent> processWait(Wait wait) {
    waitQueue.add(wait.getActor());
    return List.of(new WaitedEvent());
  }

  private List<GameEvent> processCardUse(CardUse cardUse) {
    List<GameEvent> events = new ArrayList<>();
    events.addAll(cardUse.process(map));
    events.addAll(checkEliminations());
    return events;
  }

  private List<GameEvent> checkEliminations() {
    List<GameEvent> elimEvents = new ArrayList<>();
    List<Entity> toRemove = new ArrayList<>();
    for (Entity entity : entities) {
      if (entity.getStats().getHealth() == 0.0) {
        if (entity instanceof Enemy) {
          map.removeEnemy((Enemy) entity);
          turnQueue.remove(entity);
          waitQueue.remove(entity);
          toRemove.add(entity);

          elimEvents.addAll(player.onEliminationOf(entity));
          elimEvents.add(new GridUpdatedEvent());
        } else {
          elimEvents.add(new GameOverEvent());
          game.end();
        }
      }
    }
    entities.removeAll(toRemove);
    List<Entity> enemiesLeft =
        entities.stream().filter(entity -> (entity instanceof Enemy)).toList();
    if (enemiesLeft.isEmpty()) {
      elimEvents.addAll(game.setState(State.EXPLORATION));
    }
    return elimEvents;
  }

  public CombatPhase getPhase() {
    return this.phase;
  }

  public Entity getTurn() {
    return this.currentTurn;
  }

  public List<TurnInfo> getTurnList() {
    List<Entity> turnEntities = new ArrayList<>(turnQueue);
    turnEntities.sort(turnComparator);

    List<Entity> waitEntities = new ArrayList<>(waitQueue);
    waitEntities.sort(waitComparator);

    List<TurnInfo> turnList = new ArrayList<>();

    if(currentTurn != null) {
      turnList.add(currentTurn.getTurnInfo());
    }

    for(Entity entity : turnEntities) {
      turnList.add(entity.getTurnInfo());
    }

    for(Entity entity : waitEntities) {
      turnList.add(entity.getTurnInfo());
    }

    return turnList;
  }
}