mourning-ember / src / main / java / dev / llan / model / board / GameMap.java
GameMap.java
Raw
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();
  }
}