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