summaryrefslogtreecommitdiff
path: root/scripts/entities/actors
diff options
context:
space:
mode:
authorMatheus <matheus.guedes.mg.m@gmail.com>2025-09-04 16:18:17 -0300
committerMatheus <matheus.guedes.mg.m@gmail.com>2025-09-04 16:18:17 -0300
commit9953c9a5818baa392a48a4d18339c73f3b7a814e (patch)
tree3484fbca78bfa217d84226cc774119fe054f6df9 /scripts/entities/actors
parent7dbe2bbe431f77deb3515e5cf0810cfa07215d59 (diff)
Prepare
Diffstat (limited to 'scripts/entities/actors')
-rw-r--r--scripts/entities/actors/AI/BaseAI.cs41
-rw-r--r--scripts/entities/actors/AI/BaseAI.cs.uid1
-rw-r--r--scripts/entities/actors/AI/HostileEnemyAI.cs76
-rw-r--r--scripts/entities/actors/AI/HostileEnemyAI.cs.uid1
-rw-r--r--scripts/entities/actors/Actor.cs263
-rw-r--r--scripts/entities/actors/Actor.cs.uid1
-rw-r--r--scripts/entities/actors/ActorDefinition.cs39
-rw-r--r--scripts/entities/actors/ActorDefinition.cs.uid1
-rw-r--r--scripts/entities/actors/Enemy.cs55
-rw-r--r--scripts/entities/actors/Enemy.cs.uid1
-rw-r--r--scripts/entities/actors/EnemyDefinition.cs11
-rw-r--r--scripts/entities/actors/EnemyDefinition.cs.uid1
-rw-r--r--scripts/entities/actors/Inspector.cs40
-rw-r--r--scripts/entities/actors/Inspector.cs.uid1
-rw-r--r--scripts/entities/actors/Player.cs13
-rw-r--r--scripts/entities/actors/Player.cs.uid1
-rw-r--r--scripts/entities/actors/actions/Action.cs42
-rw-r--r--scripts/entities/actors/actions/Action.cs.uid1
-rw-r--r--scripts/entities/actors/actions/BumpAction.cs32
-rw-r--r--scripts/entities/actors/actions/BumpAction.cs.uid1
-rw-r--r--scripts/entities/actors/actions/DirectionalAction.cs30
-rw-r--r--scripts/entities/actors/actions/DirectionalAction.cs.uid1
-rw-r--r--scripts/entities/actors/actions/MeleeAction.cs39
-rw-r--r--scripts/entities/actors/actions/MeleeAction.cs.uid1
-rw-r--r--scripts/entities/actors/actions/MovementAction.cs24
-rw-r--r--scripts/entities/actors/actions/MovementAction.cs.uid1
-rw-r--r--scripts/entities/actors/actions/WaitAction.cs103
-rw-r--r--scripts/entities/actors/actions/WaitAction.cs.uid1
28 files changed, 822 insertions, 0 deletions
diff --git a/scripts/entities/actors/AI/BaseAI.cs b/scripts/entities/actors/AI/BaseAI.cs
new file mode 100644
index 0000000..733a61a
--- /dev/null
+++ b/scripts/entities/actors/AI/BaseAI.cs
@@ -0,0 +1,41 @@
+using Godot;
+
+/// <summary>
+/// base para as IAs do jogo.
+/// </summary>
+public abstract partial class BaseAI : Node {
+ /// <summary>
+ /// Corpo controlado pela IA.
+ /// O corpo é a marionete da alma.
+ /// </summary>
+ protected Actor body;
+
+ public override void _Ready()
+ {
+ base._Ready();
+ // Por padrão, a IA é filha do nó de seu corpo.
+ body = GetParent<Actor>();
+ }
+
+ /// <summary>
+ /// Computa um único turno para o ator controlado.
+ /// Aviso: NPCs não possuem ações gratuitas.
+ /// A IA SEMPRE precisa executar uma ação que custe energia.
+ /// </summary>
+ public abstract void Perform();
+
+ /// <summary>
+ /// Utiliza o pathfinder do mapa para obter um caminho
+ /// da posição atual do ator para um destino qualquer.
+ /// </summary>
+ /// <param name="destination">Destino</param>
+ /// <returns>Vetor com vetores, passo a passo para chegar no destino.</returns>
+ public Godot.Collections.Array<Vector2> GetPathTo(Vector2I destination) {
+ // Arrays do Godot são muito mais confortáveis de manipular, então
+ // eu converto o Array do C# em um array do Godot antes de retornar o caminho.
+ Godot.Collections.Array<Vector2> list = [];
+ Vector2[] path = body.Map_Data.Pathfinder.GetPointPath(body.GridPosition, destination);
+ list.AddRange(path);
+ return list;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/AI/BaseAI.cs.uid b/scripts/entities/actors/AI/BaseAI.cs.uid
new file mode 100644
index 0000000..b23724c
--- /dev/null
+++ b/scripts/entities/actors/AI/BaseAI.cs.uid
@@ -0,0 +1 @@
+uid://jgm5qk02hism
diff --git a/scripts/entities/actors/AI/HostileEnemyAI.cs b/scripts/entities/actors/AI/HostileEnemyAI.cs
new file mode 100644
index 0000000..35d6d1a
--- /dev/null
+++ b/scripts/entities/actors/AI/HostileEnemyAI.cs
@@ -0,0 +1,76 @@
+using Godot;
+
+/// <summary>
+/// Uma IA simples. Sempre tentará atacar o jogador com ataques corpo a corpo.
+/// </summary>
+public partial class HostileEnemyAI : BaseAI
+{
+ /// <summary>
+ /// Caminho até a última posição conhecida do jogador.
+ /// </summary>
+ private Godot.Collections.Array<Vector2> path = [];
+
+ public override void Perform()
+ {
+ // O alvo da IA sempre é o jogador.
+ Player target = body.Map_Data.Player;
+ // Vetor que parte do inimigo até o jogador.
+ Vector2I offset = target.GridPosition - body.GridPosition;
+ // Distância entre o inimigo e o jogador. Leva em consideração somente
+ // um dos eixos.
+ int distance = int.Max(int.Abs(offset.X), int.Abs(offset.Y));
+
+ // A ação executada no turno pode ser de ataque ou de movimento.
+ Action action;
+
+ // Só faz sentido atacar o jogador se o inimigo estiver visível.
+ if (body.Map_Data.GetTile(body.GridPosition).IsInView) {
+ // Se o inimigo consegue ver que o jogador está morto,
+ // IT'S OVER.
+ if (!target.IsAlive) {
+ action = new WaitAction(body);
+ action.Perform();
+ return;
+ }
+
+ // Se estiver do lado do jogador, ataque.
+ if (distance <= 1) {
+ action = new MeleeAction(body, offset);
+ action.Perform();
+ // Executada a ação, acabamos nosso turno aqui.
+ return;
+ }
+
+ // Se o inimigo estiver visível para o jogador,
+ // consideramos que ele também consiga ver o jogador.
+ // Logo, atualizamos o caminho para a posição atual do jogador.
+ path = GetPathTo(target.GridPosition);
+ // O primeiro passo é a posição atual do inimigo, podemos remover.
+ path.RemoveAt(0);
+ }
+
+ // Se existir um caminho conhecido para o jogador.
+ if (path.Count > 0) {
+ // Pegamos o próximo passo para o destino.
+ Vector2I destination = (Vector2I) path[0];
+ // Se tiver um outro ator no caminho, paramos o nosso turno aqui.
+ if (body.Map_Data.GetBlockingActorAtPosition(destination) != null) {
+ action = new WaitAction(body);
+ action.Perform();
+ return;
+ }
+
+ // Caso o contrário, criamos uma nova ação de movimentação e a executamos.
+ action = new MovementAction(body, destination - body.GridPosition);
+ action.Perform();
+ // Podemos remover o passo do caminho.
+ path.RemoveAt(0);
+ return;
+ }
+
+ // Senão, espere.
+ action = new WaitAction(body);
+ action.Perform();
+ return;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/AI/HostileEnemyAI.cs.uid b/scripts/entities/actors/AI/HostileEnemyAI.cs.uid
new file mode 100644
index 0000000..0fa2c32
--- /dev/null
+++ b/scripts/entities/actors/AI/HostileEnemyAI.cs.uid
@@ -0,0 +1 @@
+uid://db28cxff4pl3t
diff --git a/scripts/entities/actors/Actor.cs b/scripts/entities/actors/Actor.cs
new file mode 100644
index 0000000..c77122a
--- /dev/null
+++ b/scripts/entities/actors/Actor.cs
@@ -0,0 +1,263 @@
+using Godot;
+
+/// <summary>
+/// A classe de ator define um personagem no jogo.
+/// </summary>
+[GlobalClass]
+public partial class Actor : Sprite2D
+{
+ /// <summary>
+ /// Sinal emitido toda vez que o HP mudar.
+ /// </summary>
+ /// <param name="hp">Novo HP</param>
+ /// <param name="maxHp">Quantidade máxima de HP.</param>
+ [Signal]
+ public delegate void HealthChangedEventHandler(int hp, int maxHp);
+
+ /// <summary>
+ /// Sinal emitido se o ator morrer.
+ /// </summary>
+ [Signal]
+ public delegate void DiedEventHandler();
+
+
+ /// <summary>
+ /// A definição do ator possui caracterísitcas padrões que definem
+ /// o ator em questão.
+ /// </summary>
+ protected ActorDefinition definition;
+ /// <summary>
+ /// É conveniente ter acesso ao mapa dentro do ator. Isto porque suas ações são feitas dentro
+ /// do mapa, então é necessário ter acesso à algumas informações.
+ /// </summary>
+ public MapData Map_Data { get; set; }
+
+ /// <summary>
+ /// Se o ator está vivo.
+ /// </summary>
+ public bool IsAlive { get => Hp > 0; }
+
+ private int energy;
+ /// <summary>
+ /// Utilizado no sistema de turnos.
+ /// Enquanto o ator tiver energia, ele poderá realizar turnos.
+ /// </summary>
+ public int Energy
+ {
+ get => energy;
+ set
+ {
+ if (value > Speed) {
+ energy = Speed;
+ } else {
+ energy = value;
+ }
+ }
+ }
+ /// <summary>
+ /// Taxa de recarga de energia.
+ /// </summary>
+ public int Speed { get => definition.Speed; }
+
+ /// <summary>
+ /// Executado uma vez por turno,
+ /// </summary>
+ public void RechargeEnergy() {
+ Energy += Speed;
+ }
+
+ private Vector2I gridPosition = Vector2I.Zero;
+ /// <summary>
+ /// Posição do ator no mapa do jogo. Diferentemente de Position, GridPosition tem como formato
+ /// os tiles do mapa.
+ /// </summary>
+ public Vector2I GridPosition {
+ set {
+ gridPosition = value;
+ // O sistema de coordenadas do Godot é em pixels, mas faz mais sentido para o jogo utilizar coordenadas em tiles.
+ // Esta propriedade converte um sistema para o outro automaticamente.
+ Position = Grid.GridToWorld(value);
+ }
+ get => gridPosition;
+ }
+
+ private bool blocksMovement;
+ /// <summary>
+ /// Se o ator bloqueia movimento (não pode oculpar a mesma célula de outro ator.)
+ /// </summary>
+ public bool BlocksMovement {
+ get => blocksMovement;
+ }
+
+ private string actorName;
+ /// <summary>
+ /// Nome do ator.
+ /// </summary>
+ public string ActorName {
+ get => actorName;
+ }
+
+ private int hp;
+ /// <summary>
+ /// HP máximo do ator.
+ /// </summary>
+ public int MaxHp { get; private set; }
+ /// <summary>
+ /// HP atual do ator.
+ /// </summary>
+ public int Hp {
+ get => hp;
+ set {
+ // Esta propriedade impede que o HP seja maior que o máximo.
+ hp = int.Clamp(value, 0, MaxHp);
+ EmitSignal(SignalName.HealthChanged, Hp, MaxHp);
+ if (hp <= 0) {
+ Die();
+ }
+ }
+ }
+
+ private int mp;
+ /// <summary>
+ /// Máximo de mana do ator.
+ /// </summary>
+ public int MaxMp { get; private set; }
+ /// <summary>
+ /// Mana atual do ator.
+ /// </summary>
+ public int Mp {
+ get => mp;
+ set {
+ mp = int.Clamp(value, 0, MaxMp);
+ }
+ }
+
+ /// <summary>
+ /// Estatística de ataque
+ /// </summary>
+ public int Atk { get; private set; }
+
+ /// <summary>
+ /// Estatística de defesa.
+ /// </summary>
+ public int Def { get; private set; }
+
+ /// <summary>
+ /// Estatística mental.
+ /// </summary>
+ public int Men { get; private set; }
+
+ public override void _Ready()
+ {
+ base._Ready();
+ // Quando o ator for carregado completamente, atualizamos sua posição para refletir
+ // sua posição real.
+ GridPosition = Grid.WorldToGrid(Position);
+ }
+
+ /// <summary>
+ /// Move o ator para uma localização. Veja MovementAction.
+ /// </summary>
+ /// <param name="offset">Vetor que parte da posição do ator até o seu destino.</param>
+ public void Walk(Vector2I offset) {
+ // Cada ator tem um peso no sistema de pathfinding.
+ // Sempre que ele se mover, removemos seu peso da posição antiga
+ Map_Data.UnregisterBlockingActor(this);
+ GridPosition += offset;
+ // E colocamos na próxima.
+ Map_Data.RegisterBlockingActor(this);
+ // Este peso influencia o algoritmo de pathfinding.
+ // Atores evitam caminhos bloqueados. por outros atores.
+ }
+
+ public Actor(Vector2I initialPosition, MapData map, ActorDefinition definition) {
+ GridPosition = initialPosition;
+ Map_Data = map;
+ Centered = false;
+
+ SetDefinition(definition);
+ }
+
+ /// <summary>
+ /// Recupera uma quantidade de HP do ator.
+ /// </summary>
+ /// <param name="amount">HP para recuperar</param>
+ /// <returns>Quanto HP foi realmente recuperado.</returns>
+ public int Heal(int amount) {
+ int neoHp = Hp + amount;
+
+ if (amount > MaxHp) neoHp = MaxHp;
+
+ int recovered = neoHp - Hp;
+ Hp = neoHp;
+ return recovered;
+ }
+
+ /// <summary>
+ /// Aplica uma definição de NPC para o ator.
+ /// Se o ator for um boneco de barro, este método é como um
+ /// sopro de vida.
+ /// </summary>
+ /// <param name="definition">A definição do ator.</param>
+ public virtual void SetDefinition(ActorDefinition definition) {
+ this.definition = definition;
+ blocksMovement = definition.blocksMovement;
+ actorName = definition.name;
+ ZIndex = 1;
+ Texture = definition.texture;
+
+ MaxHp = definition.Hp;
+ Hp = definition.Hp;
+ MaxMp = definition.Mp;
+ Mp = definition.Mp;
+
+ Atk = definition.Atk;
+ Def = definition.Def;
+ Men = definition.Men;
+ }
+
+ public virtual void Die() {
+ //⠀⠀⠀⠀⢠⣤⣤⣤⢠⣤⣤⣤⣤⣄⢀⣠⣤⣤⣄⠀⠀⠀⢀⣠⣤⣤⣄⠀⣤⣤⠀⠀⣠⣤⣤⣤⣤⣤⡄⢠⣤⣤⣤⣄⠀⠀
+ //⠀⠀⠀⠀⠈⢹⣿⠉⠈⠉⣿⣿⠉⠉⢾⣿⣉⣉⠙⠀⠀⢀⣾⡟⠉⠉⣿⣧⢸⣿⡄⢠⣿⠏⣿⣿⣉⣉⡁⢸⣿⡏⢉⣿⡷⠀
+ //⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⣿⣿⠀⠀⠈⠿⠿⣿⣿⡀⠀⠸⣿⡇⠀⠀⣾⣿⠀⢿⣿⣸⡿⠀⣿⣿⠿⠿⠇⢸⣿⣿⣿⣿⠀⠀
+ //⠀⠀⠀⠀⢠⣼⣿⣤⠀⠀⣿⣿⠀⠀⢷⣦⣤⣼⡿⠁⠀⠀⠹⣿⣤⣴⡿⠋⠀⠘⣿⣿⠃⠀⣿⣿⣤⣤⡄⢸⣿⡇⠙⢿⣦⡀
+ //⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⣶⣶⣶⣿⣿⣿⣿⣷⣶⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⠿⠛⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⢀⢾⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠈⠉⠉⠉⠻⢿⢿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⢠⠏⢸⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠿⢻⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⢀⠇⠀⠈⠿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⢀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⡼⠛⠳⣄⡀⠀⠐⢿⣦⡀⠀⠀⠀⢠⠃⠀⣸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠉⣳⠟⠒⠻⣿⣦⡀⠀⡘⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⢀⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠃⢠⣄⡀⠈⠙⢿⡌⠁⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⣈⢻⡿⠃⢰⠟⠲⣼⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⡰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⡶⢴⠋⠀⠀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⡴⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠞⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡴⢟⠒⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠏⠀⠀⠈⠉⣿⠇⠀⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠠⣤⣤⣀⢰⠏⠉⠙⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣠⠴⠢⠦⠽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⣿⣿⣿⣷⡄⣀⡀⠈⠉⠋⢹⠋⠁⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ //⠿⠿⠿⠿⠿⠦⠈⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
+ string deathMessage;
+
+ if (Map_Data.Player == this) {
+ deathMessage = "Você morreu!";
+ } else {
+ deathMessage = $"{ActorName} morreu!";
+ }
+
+ MessageLogData.Instance.AddMessage(deathMessage);
+
+ Texture = definition.deathTexture;
+ blocksMovement = false;
+ ZIndex = 0;
+ actorName = $"Restos mortais de {actorName}";
+ Map_Data.UnregisterBlockingActor(this);
+ EmitSignal(SignalName.Died);
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/Actor.cs.uid b/scripts/entities/actors/Actor.cs.uid
new file mode 100644
index 0000000..cf29b40
--- /dev/null
+++ b/scripts/entities/actors/Actor.cs.uid
@@ -0,0 +1 @@
+uid://c0cm4woy8lawl
diff --git a/scripts/entities/actors/ActorDefinition.cs b/scripts/entities/actors/ActorDefinition.cs
new file mode 100644
index 0000000..ab0160b
--- /dev/null
+++ b/scripts/entities/actors/ActorDefinition.cs
@@ -0,0 +1,39 @@
+using Godot;
+
+/// <summary>
+/// Define de forma genérica as características de um ator.
+/// </summary>
+[GlobalClass]
+public partial class ActorDefinition : Resource
+{
+ [ExportCategory("Visuals")]
+ // Nome do ator.
+ [Export]
+ public string name = "unnamed";
+ // Seu sprite.
+ [Export]
+ public Texture2D texture;
+ // Sprite de morto
+ [Export]
+ public Texture2D deathTexture;
+
+ [ExportCategory("Mechanics")]
+ // Se o ator bloqueia movimento.
+ [Export]
+ public bool blocksMovement = true;
+ [Export]
+ public int Speed { get; set;} = 10;
+
+ // Estatísticas padrão do ator.
+ [ExportCategory("Stats")]
+ [Export]
+ public int Hp;
+ [Export]
+ public int Mp;
+ [Export]
+ public int Atk;
+ [Export]
+ public int Def;
+ [Export]
+ public int Men;
+}
diff --git a/scripts/entities/actors/ActorDefinition.cs.uid b/scripts/entities/actors/ActorDefinition.cs.uid
new file mode 100644
index 0000000..ddcfe02
--- /dev/null
+++ b/scripts/entities/actors/ActorDefinition.cs.uid
@@ -0,0 +1 @@
+uid://crxw1e37xlrrt
diff --git a/scripts/entities/actors/Enemy.cs b/scripts/entities/actors/Enemy.cs
new file mode 100644
index 0000000..19bcd2c
--- /dev/null
+++ b/scripts/entities/actors/Enemy.cs
@@ -0,0 +1,55 @@
+using Godot;
+using System;
+
+/// <summary>
+/// Enum das diferentes IAs disponíveis.
+/// </summary>
+public enum AIType
+{
+ None,
+ DefaultHostile
+};
+
+/// <summary>
+/// Um inimigo é uma espécie de ator que é
+/// hostil ao jogador. Inimigos são controlados por IA.
+/// </summary>
+public partial class Enemy : Actor
+{
+ /// <summary>
+ /// A alma do ator. Gera ações que são executadas todo turno.
+ /// </summary>
+ public BaseAI Soul { get; private set; }
+
+ public Enemy(Vector2I initialPosition, MapData map, EnemyDefinition definition) : base(initialPosition, map, definition)
+ {
+ SetDefinition(definition);
+ }
+
+ /// <summary>
+ /// Além de definir as características gerais de um ator,
+ /// também define qual IA utilizar.
+ /// </summary>
+ /// <param name="definition">Definição do inimigo.</param>
+ public void SetDefinition(EnemyDefinition definition)
+ {
+ // Definimos as características do ator.
+ base.SetDefinition(definition);
+
+ // Definimos qual IA utilizar.
+ switch(definition.AI) {
+ case AIType.None:
+ break;
+ case AIType.DefaultHostile:
+ Soul = new HostileEnemyAI();
+ AddChild(Soul);
+ break;
+ }
+ }
+
+ public override void Die() {
+ Soul.QueueFree();
+ Soul = null;
+ base.Die();
+ }
+}
diff --git a/scripts/entities/actors/Enemy.cs.uid b/scripts/entities/actors/Enemy.cs.uid
new file mode 100644
index 0000000..93255b7
--- /dev/null
+++ b/scripts/entities/actors/Enemy.cs.uid
@@ -0,0 +1 @@
+uid://bef1fo3vgvxej
diff --git a/scripts/entities/actors/EnemyDefinition.cs b/scripts/entities/actors/EnemyDefinition.cs
new file mode 100644
index 0000000..e372e3a
--- /dev/null
+++ b/scripts/entities/actors/EnemyDefinition.cs
@@ -0,0 +1,11 @@
+using Godot;
+
+/// <summary>
+/// Além das configurações do ator, também possui qual IA utilizar.
+/// </summary>
+[GlobalClass]
+public partial class EnemyDefinition : ActorDefinition {
+ [ExportCategory("AI")]
+ [Export]
+ public AIType AI;
+} \ No newline at end of file
diff --git a/scripts/entities/actors/EnemyDefinition.cs.uid b/scripts/entities/actors/EnemyDefinition.cs.uid
new file mode 100644
index 0000000..1ba03e1
--- /dev/null
+++ b/scripts/entities/actors/EnemyDefinition.cs.uid
@@ -0,0 +1 @@
+uid://dkfdm2m2scyks
diff --git a/scripts/entities/actors/Inspector.cs b/scripts/entities/actors/Inspector.cs
new file mode 100644
index 0000000..e340543
--- /dev/null
+++ b/scripts/entities/actors/Inspector.cs
@@ -0,0 +1,40 @@
+using Godot;
+
+/// <summary>
+/// Isto é uma abominação
+/// </summary>
+public partial class Inspector : Sprite2D
+{
+ private Vector2I gridPosition = Vector2I.Zero;
+ /// <summary>
+ /// Posição do inspetor no espaço. Diferentemente de Position, GridPosition tem como formato
+ /// os tiles do mapa.
+ /// </summary>
+ public Vector2I GridPosition {
+ set {
+ gridPosition = value;
+ // O sistema de coordenadas do Godot é em pixels, mas faz mais sentido para o jogo utilizar coordenadas em tiles.
+ // Esta propriedade converte um sistema para o outro automaticamente.
+ Position = Grid.GridToWorld(value);
+ }
+ get => gridPosition;
+ }
+
+ public override void _Ready() {
+ base._Ready();
+ Camera2D camera = GetNode<Camera2D>("Camera2D");
+ camera.Enabled = true;
+ camera.MakeCurrent();
+
+ SignalBus.Instance.EmitSignal(SignalBus.SignalName.InspectorMoved, GridPosition);
+ }
+
+ /// <summary>
+ /// O Inspetor não faz parte do mapa.
+ /// </summary>
+ /// <param name="offset"></param>
+ public void Walk(Vector2I offset) {
+ GridPosition += offset;
+ SignalBus.Instance.EmitSignal(SignalBus.SignalName.InspectorMoved, GridPosition);
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/Inspector.cs.uid b/scripts/entities/actors/Inspector.cs.uid
new file mode 100644
index 0000000..ca411e4
--- /dev/null
+++ b/scripts/entities/actors/Inspector.cs.uid
@@ -0,0 +1 @@
+uid://dxsrtu4b3pi08
diff --git a/scripts/entities/actors/Player.cs b/scripts/entities/actors/Player.cs
new file mode 100644
index 0000000..324e67a
--- /dev/null
+++ b/scripts/entities/actors/Player.cs
@@ -0,0 +1,13 @@
+using Godot;
+using System;
+
+/// <summary>
+/// Classe do jogador. Por enquanto não é diferente do Ator, mas isso pode mudar.
+/// </summary>
+[GlobalClass]
+public partial class Player : Actor
+{
+ public Player(Vector2I initialPosition, MapData map, ActorDefinition definition) : base(initialPosition, map, definition)
+ {
+ }
+}
diff --git a/scripts/entities/actors/Player.cs.uid b/scripts/entities/actors/Player.cs.uid
new file mode 100644
index 0000000..8229b7f
--- /dev/null
+++ b/scripts/entities/actors/Player.cs.uid
@@ -0,0 +1 @@
+uid://c840l08453pu2
diff --git a/scripts/entities/actors/actions/Action.cs b/scripts/entities/actors/actions/Action.cs
new file mode 100644
index 0000000..f20e262
--- /dev/null
+++ b/scripts/entities/actors/actions/Action.cs
@@ -0,0 +1,42 @@
+using Godot;
+
+
+/// <summary>
+/// <c>Action</c> representa uma ação no jogo efetuada por um ator.
+/// Ações são geradas pelo jogador e pela IA, elas regem os atores do jogo.
+/// </summary>
+public abstract partial class Action : RefCounted {
+ /// <summary>
+ /// O ator que realiza a ação.
+ /// </summary>
+ protected Actor actor;
+
+ // O custo da ação.
+ protected int cost;
+
+ public Action(Actor actor) {
+ this.actor = actor;
+ // Custo base, subclasses podem sobreescrever isto se quiserem.
+ cost = 10;
+ }
+
+ /// <summary>
+ /// Método que executa a ação. Subclasses da ação devem implementar este método.
+ /// <example>
+ /// Exemplo:
+ /// <code>
+ /// Action action = new Action(actor);
+ /// /* . . . */
+ /// action.Perform();
+ /// </code>
+ /// </example>
+ /// </summary>
+ public abstract void Perform();
+
+ /// <summary>
+ /// É conveniente ter acesso ao mapa dentro de uma ação.
+ /// </summary>
+ protected MapData Map_Data {
+ get => actor.Map_Data;
+ }
+}
diff --git a/scripts/entities/actors/actions/Action.cs.uid b/scripts/entities/actors/actions/Action.cs.uid
new file mode 100644
index 0000000..9523b0a
--- /dev/null
+++ b/scripts/entities/actors/actions/Action.cs.uid
@@ -0,0 +1 @@
+uid://dlejckfyro2ch
diff --git a/scripts/entities/actors/actions/BumpAction.cs b/scripts/entities/actors/actions/BumpAction.cs
new file mode 100644
index 0000000..805520c
--- /dev/null
+++ b/scripts/entities/actors/actions/BumpAction.cs
@@ -0,0 +1,32 @@
+using Godot;
+
+/// <summary>
+/// Ação de "Esbarramento", utilizada principalmente pelo jogador.
+/// Esta ação direcionada tentará andar para o destino, se houver um
+/// ator no caminho, uma ação de ataque é gerada no lugar.
+/// </summary>
+public partial class BumpAction : DirectionalAction
+{
+ public BumpAction(Actor actor, Vector2I offset) : base(actor, offset)
+ {
+ }
+
+ // Como esta ação inevitavelmente gera outras ações,
+ // não faz sentido descontar a energia do ator.
+ public override void Perform()
+ {
+ // Declaramos uma ação genérica.
+ Action action;
+
+ // Se houver um ator no destino, crie uma ação de ataque.
+ if (GetTargetActor() != null) {
+ action = new MeleeAction(actor, Offset);
+ } else {
+ // Mas se não houver, crie uma ação de movimento.
+ action = new MovementAction(actor, Offset);
+ }
+
+ // Executa a ação.
+ action.Perform();
+ }
+}
diff --git a/scripts/entities/actors/actions/BumpAction.cs.uid b/scripts/entities/actors/actions/BumpAction.cs.uid
new file mode 100644
index 0000000..f5ce3f8
--- /dev/null
+++ b/scripts/entities/actors/actions/BumpAction.cs.uid
@@ -0,0 +1 @@
+uid://p6ij0dsuvv7y
diff --git a/scripts/entities/actors/actions/DirectionalAction.cs b/scripts/entities/actors/actions/DirectionalAction.cs
new file mode 100644
index 0000000..ca2ca95
--- /dev/null
+++ b/scripts/entities/actors/actions/DirectionalAction.cs
@@ -0,0 +1,30 @@
+using Godot;
+
+/// <summary>
+/// Ação direcionada. Esta ação é acompanhada com um vetor que representa uma
+/// distância tendo como ponto de partida o ator.
+/// </summary>
+public abstract partial class DirectionalAction : Action
+{
+ /// <summary>
+ /// Direção/distância do ator da ação.
+ /// Seu significado depende da ação que implementará esta classe.
+ /// </summary>
+ public Vector2I Offset { get; private set; }
+ /// <summary>
+ /// Coordenada do alvo da ação.
+ /// </summary>
+ public Vector2I Destination { get => actor.GridPosition + Offset; }
+ public DirectionalAction(Actor actor, Vector2I offset) : base(actor)
+ {
+ Offset = offset;
+ }
+
+ /// <summary>
+ /// Função que obtém o alvo da ação, se houver.
+ /// </summary>
+ /// <returns>O ator alvo da ação, nulo se não houver.</returns>
+ protected Actor GetTargetActor() {
+ return Map_Data.GetBlockingActorAtPosition(Destination);
+ }
+}
diff --git a/scripts/entities/actors/actions/DirectionalAction.cs.uid b/scripts/entities/actors/actions/DirectionalAction.cs.uid
new file mode 100644
index 0000000..901756a
--- /dev/null
+++ b/scripts/entities/actors/actions/DirectionalAction.cs.uid
@@ -0,0 +1 @@
+uid://cxotc2adk05j8
diff --git a/scripts/entities/actors/actions/MeleeAction.cs b/scripts/entities/actors/actions/MeleeAction.cs
new file mode 100644
index 0000000..b20430a
--- /dev/null
+++ b/scripts/entities/actors/actions/MeleeAction.cs
@@ -0,0 +1,39 @@
+using Godot;
+
+/// <summary>
+/// Ação de ataque físico. Uma ação direcionada que ataca um alvo.
+/// </summary>
+public partial class MeleeAction : DirectionalAction
+{
+ public MeleeAction(Actor actor, Vector2I offset) : base(actor, offset)
+ {
+ }
+
+ /// <summary>
+ /// Ataca o ator na direção da ação.
+ /// </summary>
+ public override void Perform()
+ {
+ // Eu te disse que este método seria útil.
+ Actor target = GetTargetActor();
+
+ // Se não houver um ator na direção, não podemos continuar.
+ // Isto é uma ação gratuita.
+ if (target == null) return;
+
+ // não podemos ter dano negativo.
+ int damage = actor.Atk - target.Def;
+
+ string attackDesc = $"{actor.ActorName} ataca {target.ActorName}";
+
+ if (damage > 0) {
+ attackDesc += $" e remove {damage} de HP.";
+ target.Hp -= damage;
+ } else {
+ attackDesc += $" mas {target.ActorName} tem músculos de aço.";
+ }
+
+ MessageLogData.Instance.AddMessage(attackDesc);
+ actor.Energy -= cost;
+ }
+}
diff --git a/scripts/entities/actors/actions/MeleeAction.cs.uid b/scripts/entities/actors/actions/MeleeAction.cs.uid
new file mode 100644
index 0000000..bc97619
--- /dev/null
+++ b/scripts/entities/actors/actions/MeleeAction.cs.uid
@@ -0,0 +1 @@
+uid://vbptt0gl1ud0
diff --git a/scripts/entities/actors/actions/MovementAction.cs b/scripts/entities/actors/actions/MovementAction.cs
new file mode 100644
index 0000000..3608357
--- /dev/null
+++ b/scripts/entities/actors/actions/MovementAction.cs
@@ -0,0 +1,24 @@
+using Godot;
+
+/// <summary>
+/// Ação de movimento. Movimenta o ator para a direção de seu Offset.
+/// </summary>
+public partial class MovementAction : DirectionalAction
+{
+ public MovementAction(Actor actor, Vector2I offset) : base(actor, offset)
+ {
+ }
+
+ public override void Perform()
+ {
+ // Não anda se o destino for um tile sólido.
+ if (!Map_Data.IsTileWalkable(Destination)) return;
+
+ // Não anda se o destino for oculpado por um ator.
+ // Na maioria dos casos, essa condição nunca é verdadeira.
+ if (GetTargetActor() != null) return;
+
+ actor.Walk(Offset);
+ actor.Energy -= cost;
+ }
+}
diff --git a/scripts/entities/actors/actions/MovementAction.cs.uid b/scripts/entities/actors/actions/MovementAction.cs.uid
new file mode 100644
index 0000000..07569ef
--- /dev/null
+++ b/scripts/entities/actors/actions/MovementAction.cs.uid
@@ -0,0 +1 @@
+uid://cdtpdaeg7hh6p
diff --git a/scripts/entities/actors/actions/WaitAction.cs b/scripts/entities/actors/actions/WaitAction.cs
new file mode 100644
index 0000000..2d3ccc2
--- /dev/null
+++ b/scripts/entities/actors/actions/WaitAction.cs
@@ -0,0 +1,103 @@
+using Godot;
+
+/// <summary>
+/// Ação da inação. Ação que realiza nada.
+/// </summary>
+public partial class WaitAction : Action
+{
+ public WaitAction(Actor actor) : base(actor)
+ {
+ }
+
+ public override void Perform()
+ {
+//-=-==============================================-======++++*@@%*+#%%+*%%#*#%%@%**%%#=:+%@%=--.....::::@@@:................... #@----::@@@@@@@@@@@@@
+//--============--===========================================+@. :@ =% @ :* @#= @@*........@@@:................ .::::::@@@@@@@@@@@@
+//===========================================================+@+ * :+ @ :*%* @%+ @=.......:@@@:.......... ...... ..::.:@@@@@@@@@@@
+//===-===========---========--=========----==========--=======%% . . @ =%@* @%+ @@:......@@@:.......... ....... .....:*@@@@@@@@@@
+//=========-==========================--==============-=======#@ #@ #* @%+ @@:.. .@@@:. ........ ..... ..:*@@@@*@@@@@@@@@
+//============================--====---=======================*@ * @@ %@@* @%+ @=. @@@... . =@@@: #@@@@@@@@
+//===--==-========----==---=----==----===========-===-==#@@@@@@@- @ @@ :#%* #*+ *#*: .@@@: . .. .@@@@@@@@
+//-------==--==-==---::---======----===-----==--===---===*%@@@@@* @: @@ * = %= %@@. .. =@@@@@@@
+//--==---==-=--=====----======-=----==-===---=======--========@@@@@%*=+#%#*-=*%%%%**#%%%#*#%%%*=.%@@: @@@@@@@
+//===-=-----=--=--====--======--====-===-=--==----========--==@@@@@: #@@: . . :=*@@@@@@@
+//==--==-===-==-==@@+====--=----==-==-==---=------===========@@@@@@@@@- #@@: . .. :@@@@@@@+@@@@@@
+//==---=--=======@%@*@%-==-==-==-========--==----========-===@ .*@@@@+ #@@: . @@@@@@
+//========-====--==@@%@@+@%===-=--======--:-====-===========%+ .=@@@@%. #@@: . . =@@@@@
+//---=-=-==-----=====*@==@@*@@+==-----=-----====--===@@@*==*@ -@@@@@- :*##=*@@- . . .@@@@@
+//------------===---===%@*=@%=*@*==------------==-=--%@@@@@@: .. ..:#@@@@*:@@@@@@@@@% .@@@@@
+//---==-----===-====-==-==@#=#@@+@@===----==--=====--====%@* ..............+@@@@@@@@@@@@@@. ....:::::. --:+:%: @=:@@@. .::-====@@@@@
+//==-=----==----===--==-----=@-=@@+@#@%=-==-======---====#@ ....:::.::......@@@@@@@@@@@@@% . ..-@#.@:%@*@%.@+ %*:@@*@.@@#@*@=%@# %@@@@@@*@@@@@
+//===----=======-====--=--====-+*%@+@@@=@=--=====--=====+@ .. .+@@@@@@@@@@:...@@@@@@@@@@@@@% -@#@@-@- =@ @% *@.#@.@.@ *@*=@@@% @@@@:
+//====--==-===-========---=========@%*@@%%@@#====--====*@: .=@@@@@@%=+: @*::.@@@@@@@@@@@@@@: :@::@=:%@#:.-- :: .: : @@@.
+//===---===-==-------:-----=-=====---=@+@@***=========+@: =@@@ : @*=.:@@@@@@@@@@@@@@@ . =@:@@%
+//==----===-===-=------==----==--=--====*@@@*==*@@#==+@: . =@ - . #@@*.. +@@@@@@@= . ## -@
+//===---=-==-----=----===----============-======@@@@@@. ..:@= : =@@++== .@@+ . .. -@@@%#@=
+//=-=--==-===--------==-=---=---=----------=======*@@. *@@@-@@*@ #= * . . ..:@@@@:
+//=====-===-=======-==----====-----=======--==-===%@ .. .=@ = #. .. .. .@: %@@-
+//==-==-==-=--===-=====-------=----==--=====-=--=@* .. .* =. :* + .. . %:
+//==-==----=-------=------=----==-----==========@: ......+ #%*** @@%%+: . %@=
+//===--=-----------=--=--==---===---:=@@@*==-=@@. ....::@ .. %@*:%*-#%#*=: . :=:=%#@@@+
+//==---=-:-====---========-----====-:-=@@@@++@= ....@@%* @-@@+=%%. -@%: -@@@. :
+//==----========---------------==========@@@* %@@@:=%@@*@@@@==+@==- .@= .*@- #*
+//==-===--===-=---:-----------===========@% :%@=*@@@@@@@@@@@@===%=======+@#+=@#%@ .@@%=
+//------=====-------=-:----=#*=========@% :%-.@==@@@@@@@@@@@@++@*=======*@ *= *@@@=. =*=*@@:
+//===--=---===------------=*@@@======@% .%- .#%==#@@@@@@@@@@@%+#@@%=====*@ *+*-#@@@- .. . :. %:
+//--=======--=-=-----:---=--+@@@=-=@% :%.:==+@*==@@@@@@@@@@@@=---##====*@. *:=@ *@. . . :@@@%@
+//===---=---==---------------=@@@@= =@=======#%-@@@@@@@@@@@@=--*@====*@: @:+@@%.@- . . .@@@@@+ =@*=%=#-.
+//=====-----===@@*-----------=@@ *@* =@@=====@+--%@@@@@@@@@@@*-==@====+@= % :@@%@= =#. :. : -*%+#:#
+//-=--=--------@@@==---=--+@@@@@@ +@@@= =#%*===-@=--=@@@@@@@@@@@@+-=@=====@+: :@ :. :@@: %*
+//*@#==--------=@@@----*@@@@@@@@@@@+ -+=@*===%*--=@@@@@@@@@@@@@=-@*-===%*- : =+ =@%@:.: *%. %*
+//%@@+----------+@@@%@@@@@@@@@@@@@@: =*==@+-==@=--@@@@@@@@@@@@@--@*-===%*=:% #: -@@#-: .@@@= +@@@@@%
+//+@@#-------==+@@@@@@@@@@@@@@@@@@@@# :: =====@=--=@-=+@@@@@@@@@@@@@-%*====*#=+% @: .:%@@:. *@@%@@@%
+//=@@@=---=@@@@@@@@@@@@@@@@@@@@@@@@@@@. =@@= ======@=-=%+==@@@@@@@@@@@@@*=#=====@=%* % ..#@@@. -@@@@@
+//=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*=@@* ==-===#*---@*-%@@@@@@@@@@@@@=%=--==@#@=%%. :@ ::*@-%: -#. %@@@@@@.
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* :+-====@=---%%=@@@@@@@@@@@@@%*=-====@*@= @: -@@+=- :@@* +@@@@@@@@@@@@=
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@: =@- ==.==--=@----*%%@@@@@@@@@@@@@*=-====#@==. @: %@=:@= =@@= =@@@@@@@@@@@
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. .@@% =:.---==%=---=%#@@@@@@@@@@@@@%==-====@+=. *: #%%@.: #@@@@@@@@@@@@@@:
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@::@@- =- --=--*%-----@@@@@@@@@@@@@@@==---===@=. -: :. .@# =@@@@@@@@@@@@@@@@. *@%
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%. @@:= :==---@=----=@@@@@@@@@@@@@@@=--====#* @: . *@@ #@@@@@@@@@@@@@@@@@@@@@-
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@: @@= % ------@=----#@@@@@@@@@@@@@@*-======%+ @ +@. -@@.:@@@@@@@@@@@@@@@@@@@@@@:
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%: @@= .% .-----=@+=---@@@@@@@@@@@@@@@=--==== *: *: :@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@ :=
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= %.:------@=---*@@@@@@@@@@@@@@@==-==: *= * *@* =@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:@@@*
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%: =*:-----@----+@@@@@@@@@@@@@@@#-====. -@= @@=-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=.@*----%----+@@@@@@@@@@@@@@@@======. +* .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -%
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=-:@----=@@@@@@@@@@@@@@@@@====== -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ :@@#
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%----@@@@@@@@@@@@@@@@@%====== :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%
+//#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%----%@@@@@@@@@@@@@@@@@*=====- -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//++=+*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%----*@@@@@@@@@@@@@@@@@@+=-==== :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=
+//=====++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%-----@@@@@@@@@@@@@@@@@@@=-=-==- -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//+===++@@%==*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%-----@@@@@@@@@@@@@@@@@@@@=---=-- :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//++=++%@@+++=++%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%-----#@@@@@@@@@@@@@@@@@@@*======: :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//===++@@==+++====*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-----*@@@@@@@@@@@@@@@@@@@@*--=---. .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=+++=+++++++++++*@@%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=-==-=@@@@@@@@@@@@@@@@@@@@@===----: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//===++==+++++++=%@@+==*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+=====@%@@@@@@@@@@@@@@@@@@@@=-----=: %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//++=====++++===*@%====+==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*=====@*@@@@@@@@@@@@@@@@@@@@@-------: :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//++===+++++====++++========@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*=====@+=@@@@@@@@@@@@@@@@@@@@@=------=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=========+==++++=========+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%=====#@=*@@@@@@@@@@@@@@@@@@@@%%----=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=+++++========+========+@@@*=+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%=====+@@@@@@@@@@@@@@@@@@@@@@@@=@+-=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//++++++====++=====*@@%==%@*=====#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@ %=====+% @@@@@@@@@@@@@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//==+==+===+++++==*@@=*@@#+=======+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@***%@@*=*@@%**%@@%**@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=++==+==++++++++%@@=@*@@%=========*@@@@@@@ %* %+ @= :@+ * @ =@@ @@@@. *# @ %@ # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=======++++++#@@+%@@@@@#=========%@@@@@@@@: :: #: :% @ @+ * @ =@@ -: @@@@ @= * @ % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//===++==+++%@%*+%@@*+**+==+====+@@@%==%@@@@+ = %* @ %= * @ =@@ -= @@@@: .@: * @ % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=+=+====++@@@@@@@@@%=====+==+==#*====+*@@@@ @* @ %= * @ =@@ -= @@@@@ @@: * @ % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=======%@@*+@@%+++++++========+======+=+@@@ - @* @ %= * @ =@@ -= @.%@@: :@@: * @ % @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//==+=+@@**@@@+*@@==++==+===+=========+++++@@ @ @* % @+ + @ :%@ -. @ @@: :@@- + @ + -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//======%@@+*@@@++=++==++===++========+++++#@- @ :@@= -@@ %@ * +* @: :@@@ @@% @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=+@@#@@@@@@=============++=+++=+==+=+%@@@@%@@@@@%=***+=%%@@@%*: -*%%%+:.=@@@*++*#***=. .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//@%+%@@%==*+============+*%@@%*+*%@@@@@@*%@@@@@@@@%#*=+#**#+%*%##*+=*#####+:@@######***#*=*#**@@@@@@@@@@@@@@@#*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//@%==+@@@===============*@ @%% @@+ @* @ *= =% % #+@ % @ -+ @@@= . @ -@ %+ @@@@@@@@@@@@@@@@@@@@@@@@
+//%@@*=================+=*@. @@ :. @ % =# * @. ** @#- =+*:**= =*% @ -* *#@@@@*. *%: @ @ %* *#@@@@@@@@@@@@@@@@@@@@@@@@
+//==@@#===+++======++=+==*@. @@ =: @ @ :# %= %. %@* %= =@ @@% @ =* @@@@@@@= @@: @ . %+ @@@@@@@@@@@@@@@@@@@@@@@@@@
+//%========+====+%@@%+=+=*@. @@ =: @ @ :% @: :@ - =@* %- -@ @@% -* @@@@@= @@: @ %+ @@@@@@@@@@@@@@@@@@@@@@@@
+//**@@@@@%*%@@@@@@*===++=*@. @@ =: @ @ :% =% -% : @* %: -@ @@@ @ =* @%- +@@= @@: @ + . %+ @@@@@@@@@@@@@@@@@@@@@@@@@@
+//@@@@@@@@@@@@@@*======+=*@. *# :. @ % -# * @-+* @# %: -@ @@@ @ =* +**-@@@= @@: @ @ :. %+ *#@@@@@@@@@@@@@@@@@@@@@@@@
+//@@@@@@@@@@@@*====+==++=*@. @ @@* .@* @ .*#. @ @* #: -@ @@% @ -+ =*%@@= @@: @ @ +. #+ *@@@@@@@@@@@@@@@@@@@@@@@
+//@@@@@@@@@@@======++====+*%@@@@%%%@@@@%*+#@@@@@%#%@@#=+**=. =**+=***==*#*: :%@@@@@@@@@**=+#*==*###*=. -=**+::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//@@@@@@@@@@+==+==++=====++=====++=+++++++++===+++++#% +@@@* :#%= =@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ actor.Energy -= cost;
+ }
+
+
+} \ No newline at end of file
diff --git a/scripts/entities/actors/actions/WaitAction.cs.uid b/scripts/entities/actors/actions/WaitAction.cs.uid
new file mode 100644
index 0000000..120c8c1
--- /dev/null
+++ b/scripts/entities/actors/actions/WaitAction.cs.uid
@@ -0,0 +1 @@
+uid://c24ebgrcsn6yi