package dev.llan.model.board;
import dev.llan.events.DisplayUpdatedEvent;
import dev.llan.model.Game;
import dev.llan.model.board.generation.MapInitialization;
import dev.llan.model.board.pathfinding.Path;
import dev.llan.model.board.Tile.TileType;
import dev.llan.events.GameEvent;
import dev.llan.events.GridUpdatedEvent;
import dev.llan.model.pieces.Entity;
import dev.llan.model.pieces.GamePieces;
import dev.llan.model.pieces.inanimates.Inanimate;
import dev.llan.model.pieces.enemies.Enemy;
import dev.llan.model.pieces.player.Player;
import dev.llan.model.state.State;
import java.util.*;
public class GameMap implements Board {
private final Game game;
private final Grid grid;
private EntityPosition playerPos;
private final Map<Enemy, EntityPosition> enemyPosMap;
private State gameState;
private final List<Point> highlightedPoints;
private final MapInitialization initializer;
public GameMap(Game game, int size) {
this.game = game;
grid = new Grid(size);
enemyPosMap = new HashMap<>();
highlightedPoints = new ArrayList<>();
initializer = new MapInitialization(game, this);
}
@Override
public GamePieces init() {
GamePieces pieces = initializer.generateMap(Optional.empty());
Player player = pieces.player();
playerPos = new EntityPosition(pieces.player(), pieces.player().getPosition());
for (Enemy enemy : pieces.enemies()) {
enemyPosMap.put(enemy, new EntityPosition(enemy, enemy.getPosition()));
}
grid.updateVisibilities(player.getPosition(), player.getStats().getScoutingRadius());
return pieces;
}
public GamePieces initNext(Player player) {
GamePieces pieces = initializer.generateMap(Optional.of(player));
playerPos = new EntityPosition(player, player.getPosition());
enemyPosMap.clear();
for (Enemy enemy : pieces.enemies()) {
enemyPosMap.put(enemy, new EntityPosition(enemy, enemy.getPosition()));
}
grid.updateVisibilities(player.getPosition(), player.getStats().getScoutingRadius());
return pieces;
}
public void updateGameState(State gameState) {
this.gameState = gameState;
}
public Grid getGrid() {
return grid;
}
@Override
public int getWidth() {
return grid.getColSize();
}
@Override
public int getHeight() {
return grid.getRowSize();
}
public List<GameEvent> setTileHighlight(Point point, boolean highlighted){
grid.tileAt(point).setHighlighted(highlighted);
highlightedPoints.add(point);
return List.of(new DisplayUpdatedEvent());
}
public List<GameEvent> clearAllHighlightedTiles() {
for(Point point : highlightedPoints) {
grid.tileAt(point).setHighlighted(false);
}
highlightedPoints.clear();
return List.of(new DisplayUpdatedEvent());
}
public List<GameEvent> moveEntity(Entity entity, Path path) {
List<GameEvent> events = new ArrayList<>();
State startingState = gameState;
while (!path.isEmpty()) {
if (startingState != gameState) {
break;
}
Point point = path.getFirst();
double movementCost = grid.getMovementCost(point, entity.getPosition());
if (entity.canMove(movementCost)) {
path.removeFirst();
if (entity instanceof Player) {
if (path.isEmpty()) {
((Player) entity).removeActivePath();
}
}
switch (gameState) {
case EXPLORATION -> events.addAll(moveExploration((Player) entity, point));
case COMBAT -> events.addAll(moveCombat(entity, point, movementCost));
}
} else {
break;
}
}
return events;
}
private List<GameEvent> moveExploration(Player player, Point newPos) {
Tile tile = grid.tileAt(newPos);
if (tile.getType() == TileType.VACANT) {
return movePlayerTo(player, newPos);
} else if (tile.getType() == TileType.INTERACTABLE) {
Inanimate inanimate = grid.tileAt(newPos).getInanimate().get();
return inanimate.interact();
}
return List.of();
}
private List<GameEvent> moveCombat(Entity entity, Point newPos, double movementCost) {
Tile tile = grid.tileAt(newPos);
entity.reduceMovement(movementCost);
if (tile.getType() == TileType.VACANT) {
if (entity instanceof Player) {
return movePlayerTo((Player) entity, newPos);
} else {
return moveEnemyTo((Enemy) entity, newPos);
}
} else if (tile.getType() == TileType.INTERACTABLE) {
Inanimate inanimate = grid.tileAt(newPos).getInanimate().get();
return inanimate.interact();
}
return List.of();
}
private List<GameEvent> movePlayerTo(Player player, Point newPos) {
Tile prev = grid.tileAt(player.getPosition());
prev.removeEntity();
Tile current = grid.tileAt(newPos);
current.addEntity(player);
player.setPosition(newPos);
playerPos.setPoint(newPos);
grid.updateVisibilities(newPos, player.getStats().getScoutingRadius());
return generateMovementEvent();
}
private List<GameEvent> moveEnemyTo(Enemy enemy, Point newPos) {
Tile prev = grid.tileAt(enemy.getPosition());
prev.removeEntity();
Tile current = grid.tileAt(newPos);
current.addEntity(enemy);
enemy.setPosition(newPos);
EntityPosition enemyPos = enemyPosMap.get(enemy);
enemyPos.setPoint(newPos);
return generateMovementEvent();
}
private List<GameEvent> generateMovementEvent() {
List<GameEvent> events = new ArrayList<>();
if (gameState == State.EXPLORATION) {
for (EntityPosition positions : enemyPosMap.values()) {
Enemy enemy = (Enemy) positions.getEntity();
if (enemy.withinRadius(playerPos.getPoint())) {
enemy.setInCombat();
events.add(new GridUpdatedEvent());
events.addAll(game.setState(State.COMBAT));
return events;
}
}
}
events.add(new GridUpdatedEvent());
return events;
}
public void removeEnemy(Enemy enemy) {
Point enemyPos = enemy.getPosition();
grid.tileAt(enemyPos).removeEntity();
enemyPosMap.remove(enemy);
game.removeEnemy(enemy);
}
public Grid cloneGrid() {
return this.grid.clone();
}
}