using Godot;
///
/// Classe que cuida dos dados e da parte lógica do mapa.
/// O mapa é o cenário onde as ações do jogo ocorrem.
/// Mais especificamente, o mapa é um único andar da masmorra.
///
public partial class MapData : RefCounted
{
public static readonly TileDefinition wallDefinition = GD.Load("res://assets/definitions/tiles/wall.tres");
public static readonly TileDefinition floorDefinition = GD.Load("res://assets/definitions/tiles/floor.tres");
///
/// Largura do mapa.
///
public int Width { get; private set; }
///
/// Altura do mapa.
///
public int Height { get; private set; }
///
/// Os tiles que compõem o mapa.
///
public Godot.Collections.Array Tiles { get; private set; } = [];
///
/// O jogador é especial e por isso o mapa faz questão de rastreá-lo.
///
public Player Player { get; set; }
///
/// Lista de todos os atores dentro do mapa.
///
public Godot.Collections.Array Actors { get; private set; } = [];
private AStarGrid2D pathfinder;
///
/// Objeto do Godot que utiliza do algoritmo A* para calcular
/// caminhos e rotas.
///
public AStarGrid2D Pathfinder { get => pathfinder; }
///
/// Peso do ator no pathfinder.
/// A IA irá evitar de passar por espaços com peso alto.
///
private static float ActorWeight = 10.0f;
///
/// Inicializa o pathfinder;
///
public void SetupPathfinding() {
pathfinder = new AStarGrid2D
{
//A região é o mapa inteiro.
Region = new Rect2I(0, 0, Width, Height)
};
// Atualiza o pathfinder para a região definida.
pathfinder.Update();
// Define quais pontos do mapa são passáveis ou não.
for (int y = 0; y < Height; y++) {
for (int x = 0; x < Width; x++) {
Vector2I pos = new Vector2I(x, y);
Tile tile = GetTile(pos);
// Pontos sólidos são impossíveis de passar.
pathfinder.SetPointSolid(pos, !tile.IsWalkable);
}
}
// Registra todos os atores em cena.
foreach (Actor actor in Actors) {
if (actor.BlocksMovement) {
RegisterBlockingActor(actor);
}
}
}
///
/// Define um peso na posição de um ator para que a IA evite de passar por lá.
/// Ênfase em evitar. Se o único caminho para o destino estiver bloqueado
/// por um ator, o jogo tentará andar mesmo assim.
///
/// O ator em questão.
public void RegisterBlockingActor(Actor actor) {
pathfinder.SetPointWeightScale(actor.GridPosition, ActorWeight);
}
///
/// Remove o peso na posição de um ator.
/// Quando um ator move sua posição, devemos tirar o peso de sua posição anterior.
///
/// O ator em questão.
public void UnregisterBlockingActor(Actor actor) {
pathfinder.SetPointWeightScale(actor.GridPosition, 0);
}
public MapData(int width, int height, Player player) {
Width = width;
Height = height;
Player = player;
// Como o jogador é criado antes do mapa, precisamos
// atualizá-lo com o novo mapa.
player.Map_Data = this;
InsertActor(player);
SetupTiles();
}
///
/// Cria novos Tiles até preencher as dimensões do mapa.
/// É importante que estes tiles sejam paredes, o gerador de mapas
/// não cria paredes por conta própria.
///
private void SetupTiles() {
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
Tiles.Add(new Tile(new Vector2I(j, i), wallDefinition));
}
}
}
///
/// Registra um ator no mapa. A existência de um ator não é considerada se ele não
/// estiver registrado no mapa.
///
/// O ator em questão
public void InsertActor(Actor actor) {
Actors.Add(actor);
}
///
/// Converte uma coordenada em um índice para acessar a lista de tiles.
///
/// Vetor posição
/// Índice na lista de tiles. -1 se estiver fora do mapa.
private int GridToIndex(Vector2I pos) {
if (!IsInBounds(pos)) return -1;
return pos.Y * Width + pos.X;
}
///
/// Se uma coordenada está dentro da área do mapa.
///
/// Vetor posição
/// Se o vetor está dentro do mapa.
private bool IsInBounds(Vector2I pos) {
if (pos.X < 0 || pos.Y < 0) {
return false;
}
if (pos.X >= Width || pos.Y >= Height) {
return false;
}
return true;
}
///
/// Obtém o tile na posição desejada.
///
/// Vetor posição
/// O tile na posição, nulo se for fora do mapa.
public Tile GetTile(Vector2I pos) {
int index = GridToIndex(pos);
if (index < 0) return null;
return Tiles[index];
}
///
/// Obtém o tile na posição desejada.
///
/// x da coordenada
/// y da coordenada
/// O tile na posição, nulo se for fora do mapa.
public Tile GetTile(int x, int y) {
return GetTile(new Vector2I(x, y));
}
///
/// Obtém o ator na posição especificada.
///
/// Vetor posição
/// O ator na posição especificada, nulo se não houver.
public Actor GetBlockingActorAtPosition(Vector2I pos) {
foreach (Actor actor in Actors) {
if (actor.GridPosition == pos && actor.BlocksMovement) {
return actor;
}
}
return null;
}
///
/// Obtém todos os atores na posição especificada.
/// É possível haver mais de um ator na mesma posição se um deles for morto.
///
/// Vetor posição
/// Lista com todos os atores na posição especificada.
public Godot.Collections.Array GetActorsAtPosition(Vector2I pos) {
Godot.Collections.Array ZOfZero = [];
Godot.Collections.Array ZOfOne = [];
Godot.Collections.Array ZOfTwo = [];
// Pego todos os atores
foreach (Actor actor in Actors) {
if (actor.GridPosition == pos) {
switch (actor.ZIndex) {
case 0:
ZOfZero.Add(actor);
break;
case 1:
ZOfOne.Add(actor);
break;
case 2:
ZOfTwo.Add(actor);
break;
}
}
}
// Retorno os atores ordenados por ZIndex.
return ZOfZero + ZOfOne + ZOfTwo;
}
///
/// Verifica se é possível caminhar na coordenada especificada.
/// Este método será removido.
///
/// Vetor posição
/// Se é possível caminhar nesta posição
public bool IsTileWalkable(Vector2I pos) {
Tile tile = GetTile(pos);
if (tile == null) return false;
return tile.IsWalkable;
}
}