summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Entities/Actions/EscapeAction.cs1
-rw-r--r--scripts/Entities/Actors/Actor.cs49
-rw-r--r--scripts/Entities/Actors/Enemy.cs32
-rw-r--r--scripts/Entities/Actors/Inventory.cs34
-rw-r--r--scripts/Entities/Actors/Player.cs32
-rw-r--r--scripts/Entities/Entity.cs25
-rw-r--r--scripts/Entities/Items/Item.cs24
-rw-r--r--scripts/Entities/Items/ItemEntity.cs40
-rw-r--r--scripts/GUI/MainMenu.cs2
-rw-r--r--scripts/Game.cs41
-rw-r--r--scripts/GameManager.cs21
-rw-r--r--scripts/Magic/SpellBook.cs30
-rw-r--r--scripts/Map/DungeonGenerator.cs2
-rw-r--r--scripts/Map/Map.cs18
-rw-r--r--scripts/Map/MapData.cs120
-rw-r--r--scripts/Map/Tile.cs49
-rw-r--r--scripts/Utils/ISaveable.cs9
-rw-r--r--scripts/Utils/ISaveable.cs.uid1
18 files changed, 510 insertions, 20 deletions
diff --git a/scripts/Entities/Actions/EscapeAction.cs b/scripts/Entities/Actions/EscapeAction.cs
index cc5d289..f0b1ed5 100644
--- a/scripts/Entities/Actions/EscapeAction.cs
+++ b/scripts/Entities/Actions/EscapeAction.cs
@@ -7,6 +7,7 @@ public partial class EscapeAction(Actor actor) : Action(actor)
{
public override bool Perform()
{
+ Actor.MapData.SaveGame();
SignalBus.Instance.EmitSignal(SignalBus.SignalName.EscapeRequested);
return false;
}
diff --git a/scripts/Entities/Actors/Actor.cs b/scripts/Entities/Actors/Actor.cs
index ec00e30..71a32a5 100644
--- a/scripts/Entities/Actors/Actor.cs
+++ b/scripts/Entities/Actors/Actor.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Magic;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Utils;
@@ -9,7 +10,7 @@ namespace TheLegendOfGustav.Entities.Actors;
/// A classe de ator define um personagem no jogo.
/// </summary>
[GlobalClass]
-public partial class Actor : Entity
+public partial class Actor : Entity, ISaveable
{
#region Fields
private int mp;
@@ -29,6 +30,9 @@ public partial class Actor : Entity
{
SetDefinition(definition);
}
+ public Actor(Vector2I initialPosition, MapData map) : base(initialPosition, map)
+ {
+ }
#endregion
#region Signals
@@ -278,5 +282,48 @@ public partial class Actor : Entity
MapData.UnregisterBlockingEntity(this);
EmitSignal(SignalName.Died);
}
+
+ public new Dictionary<string, Variant> GetSaveData()
+ {
+ Dictionary<string, Variant> baseData = base.GetSaveData();
+ baseData.Add("energy", Energy);
+ baseData.Add("max_hp", MaxHp);
+ baseData.Add("hp", Hp);
+ baseData.Add("max_mp", MaxMp);
+ baseData.Add("mp", MaxMp);
+ baseData.Add("atk", Atk);
+ baseData.Add("def", Def);
+ baseData.Add("men", Men);
+ baseData.Add("mp_regen_rate", MpRegenRate);
+ baseData.Add("mp_regen_per_turn", MpRegenPerTurn);
+ baseData.Add("spell_book", SpellBook.GetSaveData());
+
+ return baseData;
+ }
+
+ public new bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ if (!base.LoadSaveData(saveData))
+ {
+ return false;
+ }
+
+ if (!SpellBook.LoadSaveData((Dictionary<string, Variant>)saveData["spell_book"]))
+ {
+ return false;
+ }
+
+ Energy = (int)saveData["energy"];
+ MaxHp = (int)saveData["max_hp"];
+ Hp = (int)saveData["hp"];
+ MaxMp = (int)saveData["max_mp"];
+ Mp = (int)saveData["max_mp"];
+ Atk = (int)saveData["atk"];
+ Def = (int)saveData["def"];
+ Men = (int)saveData["men"];
+ MpRegenRate = (int)saveData["mp_regen_rate"];
+ MpRegenPerTurn = (int)saveData["mp_regen_per_turn"];
+ return true;
+ }
#endregion
} \ No newline at end of file
diff --git a/scripts/Entities/Actors/Enemy.cs b/scripts/Entities/Actors/Enemy.cs
index d398176..4994534 100644
--- a/scripts/Entities/Actors/Enemy.cs
+++ b/scripts/Entities/Actors/Enemy.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Entities.Actors.AI;
using TheLegendOfGustav.Map;
@@ -8,7 +9,7 @@ namespace TheLegendOfGustav.Entities.Actors;
/// Um inimigo é uma espécie de ator que é
/// hostil ao jogador. Inimigos são controlados por IA.
/// </summary>
-public partial class Enemy : Actor
+public partial class Enemy : Actor, ISaveable
{
private EnemyDefinition definition;
@@ -17,6 +18,9 @@ public partial class Enemy : Actor
this.definition = definition;
SetDefinition(definition);
}
+ public Enemy(Vector2I initialPosition, MapData map) : base(initialPosition, map)
+ {
+ }
/// <summary>
/// A alma do ator. Gera ações que são executadas todo turno.
@@ -30,9 +34,12 @@ public partial class Enemy : Actor
/// <param name="definition">Definição do inimigo.</param>
public void SetDefinition(EnemyDefinition definition)
{
+ this.definition = definition;
// Definimos as características do ator.
base.SetDefinition(this.definition);
+ Soul?.QueueFree();
+
// Definimos qual IA utilizar.
switch (definition.AI)
{
@@ -51,4 +58,27 @@ public partial class Enemy : Actor
Soul = null;
base.Die();
}
+
+ public new Dictionary<string, Variant> GetSaveData()
+ {
+ Dictionary<string, Variant> baseData = base.GetSaveData();
+ baseData.Add("definition", definition.ResourcePath);
+
+ return baseData;
+ }
+
+ public new bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ string definitionPath = (string)saveData["definition"];
+ EnemyDefinition definition = GD.Load<EnemyDefinition>(definitionPath);
+
+ SetDefinition(definition);
+
+ if (!base.LoadSaveData(saveData))
+ {
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/scripts/Entities/Actors/Inventory.cs b/scripts/Entities/Actors/Inventory.cs
index 5ae61b4..ebc60e4 100644
--- a/scripts/Entities/Actors/Inventory.cs
+++ b/scripts/Entities/Actors/Inventory.cs
@@ -1,11 +1,12 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Entities.Items;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Utils;
namespace TheLegendOfGustav.Entities.Actors;
-public partial class Inventory(int capacity) : Node
+public partial class Inventory(int capacity) : Node, ISaveable
{
private Player player;
public int Capacity { get; private set; } = capacity;
@@ -42,4 +43,35 @@ public partial class Inventory(int capacity) : Node
{
Items.Remove(item);
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ Godot.Collections.Array<Dictionary<string, Variant>> itemsData = [];
+ foreach (Item item in Items) {
+ itemsData.Add(item.GetSaveData());
+ }
+
+ return new()
+ {
+ {"items", itemsData}
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ Array<Dictionary<string, Variant>> itemRess = (Array<Dictionary<string, Variant>>)saveData["items"];
+
+ foreach(Dictionary<string, Variant> item in itemRess)
+ {
+ Item it = new();
+ if(!it.LoadSaveData(item))
+ {
+ return false;
+ }
+
+ Items.Add(it);
+ }
+
+ return true;
+ }
} \ No newline at end of file
diff --git a/scripts/Entities/Actors/Player.cs b/scripts/Entities/Actors/Player.cs
index d6fedb9..7304e0a 100644
--- a/scripts/Entities/Actors/Player.cs
+++ b/scripts/Entities/Actors/Player.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Map;
namespace TheLegendOfGustav.Entities.Actors;
@@ -7,7 +8,7 @@ namespace TheLegendOfGustav.Entities.Actors;
/// Classe do jogador. Por enquanto não é diferente do Ator, mas isso pode mudar.
/// </summary>
[GlobalClass]
-public partial class Player : Actor
+public partial class Player : Actor, ISaveable
{
private PlayerDefinition definition;
@@ -19,8 +20,37 @@ public partial class Player : Actor
public Inventory Inventory { get; private set; }
+ public new Dictionary<string, Variant> GetSaveData()
+ {
+ Dictionary<string, Variant> baseData = base.GetSaveData();
+ baseData.Add("inventory", Inventory.GetSaveData());
+ baseData.Add("definition", definition.ResourcePath);
+
+ return baseData;
+ }
+
+ public new bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ PlayerDefinition definition = GD.Load<PlayerDefinition>((string)saveData["definition"]);
+
+ SetDefinition(definition);
+
+ if (!base.LoadSaveData(saveData))
+ {
+ return false;
+ }
+
+ if(!Inventory.LoadSaveData((Dictionary<string, Variant>)saveData["inventory"]))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
public void SetDefinition(PlayerDefinition definition)
{
+ Inventory?.QueueFree();
Inventory = new(definition.InventoryCapacity);
AddChild(Inventory);
diff --git a/scripts/Entities/Entity.cs b/scripts/Entities/Entity.cs
index b7dbcc2..76002b1 100644
--- a/scripts/Entities/Entity.cs
+++ b/scripts/Entities/Entity.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Utils;
@@ -18,7 +19,7 @@ public enum EntityType
/// <summary>
/// Classe para elementos móveis que o jogador pode interagir.
/// </summary>
-public abstract partial class Entity : Sprite2D
+public abstract partial class Entity : Sprite2D, ISaveable
{
private Vector2I gridPosition = Vector2I.Zero;
@@ -138,4 +139,26 @@ public abstract partial class Entity : Sprite2D
Type = definition.Type;
Texture = definition.texture;
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ return new()
+ {
+ {"position_x", GridPosition.X},
+ {"position_y", GridPosition.Y},
+ {"blocks_movement", BlocksMovement},
+ {"name", DisplayName},
+ {"layer", (int)Type},
+
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ GridPosition = new((int)saveData["position_x"], (int)saveData["position_y"]);
+ BlocksMovement = (bool)saveData["blocks_movement"];
+ DisplayName = (string)saveData["name"];
+ Type = (EntityType)(int)saveData["layer"];
+ return true;
+ }
} \ No newline at end of file
diff --git a/scripts/Entities/Items/Item.cs b/scripts/Entities/Items/Item.cs
index 0eeffd8..17c318c 100644
--- a/scripts/Entities/Items/Item.cs
+++ b/scripts/Entities/Items/Item.cs
@@ -1,11 +1,12 @@
using System.Reflection.Metadata;
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Entities.Actions;
using TheLegendOfGustav.Entities.Actors;
namespace TheLegendOfGustav.Entities.Items;
-public partial class Item : RefCounted
+public partial class Item : RefCounted, ISaveable
{
public Item(ItemResource definition)
@@ -14,6 +15,10 @@ public partial class Item : RefCounted
Uses = Definition.MaxUses;
}
+ public Item()
+ {
+ }
+
public ItemResource Definition { get; private set; }
public int Uses { get; set; }
@@ -58,4 +63,21 @@ public partial class Item : RefCounted
Inventory inventory = consumer.Inventory;
inventory.RemoveItem(this);
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ return new()
+ {
+ {"definition", Definition.ResourcePath},
+ {"uses", Uses}
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ Definition = GD.Load<ItemResource>((string)saveData["definition"]);
+ Uses = (int)saveData["uses"];
+
+ return true;
+ }
} \ No newline at end of file
diff --git a/scripts/Entities/Items/ItemEntity.cs b/scripts/Entities/Items/ItemEntity.cs
index e646e40..66944ce 100644
--- a/scripts/Entities/Items/ItemEntity.cs
+++ b/scripts/Entities/Items/ItemEntity.cs
@@ -1,10 +1,11 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Entities;
using TheLegendOfGustav.Map;
namespace TheLegendOfGustav.Entities.Items;
-public partial class ItemEntity : Entity
+public partial class ItemEntity : Entity, ISaveable
{
public Item Item { get; private set; }
@@ -14,7 +15,7 @@ public partial class ItemEntity : Entity
// Eu quero muito reescrever o jogo do zero, mas não tenho tempo :(
EntityDefinition sad = new()
{
- blocksMovement = true,
+ blocksMovement = false,
name = item.Definition.DisplayName,
texture = item.Definition.Icon,
Type = EntityType.ITEM,
@@ -22,4 +23,39 @@ public partial class ItemEntity : Entity
SetDefinition(sad);
}
+ public ItemEntity(Vector2I initialPosition, MapData map) : base(initialPosition, map)
+ {
+ }
+
+ public new Dictionary<string, Variant> GetSaveData()
+ {
+ Dictionary<string, Variant> baseData = base.GetSaveData();
+ baseData.Add("item", Item.GetSaveData());
+
+ return baseData;
+ }
+
+ public new bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ Item = new();
+
+ if (!Item.LoadSaveData((Dictionary<string, Variant>)saveData["item"]))
+ {
+ return false;
+ }
+ // Eu quero muito reescrever o jogo do zero, mas não tenho tempo :(
+ EntityDefinition sad = new()
+ {
+ blocksMovement = false,
+ name = Item.Definition.DisplayName,
+ texture = Item.Definition.Icon,
+ Type = EntityType.ITEM,
+ };
+
+ SetDefinition(sad);
+
+ base.LoadSaveData(saveData);
+
+ return true;
+ }
} \ No newline at end of file
diff --git a/scripts/GUI/MainMenu.cs b/scripts/GUI/MainMenu.cs
index 74592c2..fc46cd2 100644
--- a/scripts/GUI/MainMenu.cs
+++ b/scripts/GUI/MainMenu.cs
@@ -24,7 +24,7 @@ public partial class MainMenu : Control
quitButton.Pressed += OnQuitButtonPressed;
newGameButton.GrabFocus();
- bool hasSaveFile = FileAccess.FileExists("user://save.dat");
+ bool hasSaveFile = FileAccess.FileExists("user://save_game.json");
loadGameButton.Disabled = !hasSaveFile;
}
diff --git a/scripts/Game.cs b/scripts/Game.cs
index f03aa20..47102c1 100644
--- a/scripts/Game.cs
+++ b/scripts/Game.cs
@@ -14,6 +14,7 @@ namespace TheLegendOfGustav;
/// </summary>
public partial class Game : Node
{
+ private bool initialized = false;
/// <summary>
/// Definição de um jogador.
/// </summary>
@@ -41,10 +42,12 @@ public partial class Game : Node
public override void _Ready()
{
base._Ready();
-
escapeLambda = () => EmitSignal(SignalName.MainMenuRequested);
SignalBus.Instance.EscapeRequested += escapeLambda;
+ }
+ public void NewGame()
+ {
map = GetNode<Map.Map>("Map");
inputHandler = GetNode<InputHandler>("InputHandler");
@@ -67,6 +70,38 @@ public partial class Game : Node
turnManager = new(map);
MessageLogData.Instance.AddMessage("Boa sorte!");
+ initialized = true;
+ }
+ public bool LoadGame()
+ {
+ map = GetNode<Map.Map>("Map");
+
+ inputHandler = GetNode<InputHandler>("InputHandler");
+ hud = GetNode<Hud>("HUD");
+
+ // O jogador é criado pelo jogo.
+ Player player = new Player(Vector2I.Zero, null, playerDefinition);
+ Camera2D camera = GetNode<Camera2D>("Camera2D");
+ RemoveChild(camera);
+
+ player.AddChild(camera);
+
+ if (!map.LoadGame(player))
+ {
+ return false;
+ }
+
+ player.HealthChanged += (int hp, int maxHp) => hud.OnHealthChanged(hp, maxHp);
+ player.ManaChanged += hud.OnManaChanged;
+ player.Died += () => inputHandler.SetInputHandler(InputHandlers.GameOver);
+
+ map.UpdateFOV(player.GridPosition);
+
+ turnManager = new(map);
+
+ MessageLogData.Instance.AddMessage("Boa sorte!");
+ initialized = true;
+ return true;
}
public override void _Notification(int what)
@@ -90,6 +125,8 @@ public partial class Game : Node
{
base._PhysicsProcess(delta);
+ if(initialized)
+ {
Player player = map.MapData.Player;
// Pegamos uma ação do usuário
@@ -102,6 +139,8 @@ public partial class Game : Node
// Computamos um turno.
turnManager.Tick();
+
+ }
}
}
diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs
index 8bc8b6b..a17ae79 100644
--- a/scripts/GameManager.cs
+++ b/scripts/GameManager.cs
@@ -50,10 +50,29 @@ public partial class GameManager : Node
MessageLogData.Instance.ClearMessages();
Game game = (Game)SwitchToScene(gameScene);
game.MainMenuRequested += LoadMainMenu;
+ if (!game.LoadGame())
+ {
+ SwitchToScene(mainMenuScene);
+ }
+ }
+
+ private void NewGame()
+ {
+ MessageLogData.Instance.ClearMessages();
+ Game game = (Game)SwitchToScene(gameScene);
+ game.NewGame();
+ game.MainMenuRequested += LoadMainMenu;
}
private void OnGameRequest(bool load)
{
- LoadGame();
+ if (!load)
+ {
+ NewGame();
+ }
+ else
+ {
+ LoadGame();
+ }
}
}
diff --git a/scripts/Magic/SpellBook.cs b/scripts/Magic/SpellBook.cs
index 8ccba71..914add6 100644
--- a/scripts/Magic/SpellBook.cs
+++ b/scripts/Magic/SpellBook.cs
@@ -1,8 +1,9 @@
using Godot;
+using Godot.Collections;
namespace TheLegendOfGustav.Magic;
-public partial class SpellBook : Node
+public partial class SpellBook : Node, ISaveable
{
public Godot.Collections.Array<SpellResource> KnownSpells { get; private set; } = [];
@@ -17,4 +18,31 @@ public partial class SpellBook : Node
public void ForgetSpell(SpellResource spell) {
KnownSpells.Remove(spell);
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ Array<string> spellPaths = [];
+ foreach(SpellResource spell in KnownSpells)
+ {
+ spellPaths.Add(spell.ResourcePath);
+ }
+
+ return new()
+ {
+ {"spells", spellPaths}
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ Array<string> paths = (Array<string>)saveData["spells"];
+
+ foreach(string path in paths)
+ {
+ SpellResource spell = GD.Load<SpellResource>(path);
+ KnownSpells.Add(spell);
+ }
+
+ return true;
+ }
} \ No newline at end of file
diff --git a/scripts/Map/DungeonGenerator.cs b/scripts/Map/DungeonGenerator.cs
index 7e447b0..2d5ef8d 100644
--- a/scripts/Map/DungeonGenerator.cs
+++ b/scripts/Map/DungeonGenerator.cs
@@ -144,7 +144,7 @@ public partial class DungeonGenerator : Node
Tile tile = data.GetTile(pos);
if (tile == null) return;
- tile.SetDefinition(MapData.floorDefinition);
+ tile.Key = TileType.FLOOR;
}
/// <summary>
diff --git a/scripts/Map/Map.cs b/scripts/Map/Map.cs
index 8521797..04ccabd 100644
--- a/scripts/Map/Map.cs
+++ b/scripts/Map/Map.cs
@@ -1,3 +1,4 @@
+using System.Net.Http.Headers;
using Godot;
using TheLegendOfGustav.Entities;
using TheLegendOfGustav.Entities.Actors;
@@ -95,4 +96,21 @@ public partial class Map : Node2D
{
entitiesNode.AddChild(entity);
}
+
+ public bool LoadGame(Player player)
+ {
+
+ MapData = new(0, 0, player);
+
+ if (!MapData.LoadGame())
+ {
+ return false;
+ }
+
+ PlaceTiles();
+ PlaceEntities();
+
+ MapData.EntityPlaced += OnEntityPlaced;
+ return true;
+ }
}
diff --git a/scripts/Map/MapData.cs b/scripts/Map/MapData.cs
index 49e4ca0..5f96743 100644
--- a/scripts/Map/MapData.cs
+++ b/scripts/Map/MapData.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Entities;
using TheLegendOfGustav.Entities.Actors;
using TheLegendOfGustav.Entities.Items;
@@ -10,11 +11,9 @@ namespace TheLegendOfGustav.Map;
/// O mapa é o cenário onde as ações do jogo ocorrem.
/// Mais especificamente, o mapa é um único andar da masmorra.
/// </summary>
-public partial class MapData : RefCounted
+public partial class MapData : RefCounted, ISaveable
{
#region Fields
- public static readonly TileDefinition wallDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/wall.tres");
- public static readonly TileDefinition floorDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/floor.tres");
/// <summary>
/// Peso do ator no pathfinder.
/// A IA irá evitar de passar por espaços com peso alto.
@@ -275,11 +274,16 @@ public partial class MapData : RefCounted
/// </summary>
private void SetupTiles()
{
+ if (Tiles.Count > 1)
+ {
+ Tiles.Clear();
+ }
+
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
- Tiles.Add(new Tile(new Vector2I(j, i), wallDefinition));
+ Tiles.Add(new Tile(new Vector2I(j, i), TileType.WALL));
}
}
}
@@ -314,5 +318,113 @@ public partial class MapData : RefCounted
return true;
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ Array<Dictionary<string, Variant>> serializedTiles = [];
+ Array<Dictionary<string, Variant>> serializedItemEntities = [];
+ Array<Dictionary<string, Variant>> serializedEnemies = [];
+ foreach (Tile tile in Tiles)
+ {
+ serializedTiles.Add(tile.GetSaveData());
+ }
+
+ foreach (Entity ent in Entities)
+ {
+ if (ent is Enemy enemy)
+ {
+ serializedEnemies.Add(enemy.GetSaveData());
+ }
+ else if (ent is ItemEntity it)
+ {
+ serializedItemEntities.Add(it.GetSaveData());
+ }
+ }
+
+ return new()
+ {
+ {"tiles", serializedTiles},
+ {"enemies", serializedEnemies},
+ {"items", serializedItemEntities},
+ {"player", Player.GetSaveData()},
+ {"width", Width},
+ {"height", Height},
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ Width = (int)saveData["width"];
+ Height = (int)saveData["height"];
+
+ SetupTiles();
+
+ Array<Dictionary<string, Variant>> serializedTiles = (Array<Dictionary<string, Variant>>)saveData["tiles"];
+ Array<Dictionary<string, Variant>> serializedItemEntities = (Array<Dictionary<string, Variant>>)saveData["items"];
+ Array<Dictionary<string, Variant>> serializedEnemies = (Array<Dictionary<string, Variant>>)saveData["enemies"];
+
+ for (int i = 0; i < serializedTiles.Count; i++)
+ {
+ if (!Tiles[i].LoadSaveData(serializedTiles[i]))
+ {
+ return false;
+ }
+ }
+ SetupPathfinding();
+ if (!Player.LoadSaveData((Dictionary<string, Variant>)saveData["player"]))
+ {
+ return false;
+ }
+
+ Player.MapData = this;
+
+ foreach(Dictionary<string, Variant> enemy in serializedEnemies)
+ {
+ Enemy en = new(Vector2I.Zero, this);
+ if (!en.LoadSaveData(enemy))
+ {
+ return false;
+ }
+
+ InsertEntity(en);
+ }
+
+ foreach(Dictionary<string, Variant> item in serializedItemEntities)
+ {
+ ItemEntity en = new(Vector2I.Zero, this);
+
+ if (!en.LoadSaveData(item))
+ {
+ return false;
+ }
+
+ InsertEntity(en);
+ }
+
+ return true;
+ }
+
+ public void SaveGame()
+ {
+ using var saveFile = FileAccess.Open("user://save_game.json", FileAccess.ModeFlags.Write);
+ var saveData = GetSaveData();
+ string saveString = Json.Stringify(saveData);
+
+ saveFile.StoreLine(saveString);
+ }
+
+ public bool LoadGame()
+ {
+ using var saveFile = FileAccess.Open("user://save_game.json", FileAccess.ModeFlags.Read);
+ string saveString = saveFile.GetLine();
+ var saveData = (Dictionary<string, Variant>)Json.ParseString(saveString);
+
+ if (!LoadSaveData(saveData))
+ {
+ return false;
+ }
+
+ return true;
+ }
#endregion
}
diff --git a/scripts/Map/Tile.cs b/scripts/Map/Tile.cs
index 488d124..6790f3e 100644
--- a/scripts/Map/Tile.cs
+++ b/scripts/Map/Tile.cs
@@ -1,16 +1,41 @@
+using System.Threading;
using Godot;
+using Godot.Collections;
using TheLegendOfGustav.Utils;
namespace TheLegendOfGustav.Map;
+public enum TileType
+{
+ WALL,
+ FLOOR
+}
+
/// <summary>
/// O mundo do jogo é composto por Tiles.
/// Um tile é um quadrado de 16x16 que representa uma
/// unidade discreta do cenário. Tiles podem agir como
/// parede, chão, ou outras funções.
/// </summary>
-public partial class Tile : Sprite2D
+public partial class Tile : Sprite2D, ISaveable
{
+ private static readonly Godot.Collections.Dictionary<TileType, TileDefinition> Types = new()
+ {
+ {TileType.WALL, GD.Load<TileDefinition>("res://assets/definitions/tiles/wall.tres")},
+ {TileType.FLOOR, GD.Load<TileDefinition>("res://assets/definitions/tiles/floor.tres")}
+ };
+
+ TileType key;
+ public TileType Key
+ {
+ get => key;
+ set
+ {
+ key = value;
+ SetDefinition(Types[value]);
+ }
+ }
+
private bool isExplored = false;
private bool isInView = false;
@@ -19,7 +44,7 @@ public partial class Tile : Sprite2D
/// </summary>
private TileDefinition definition;
- public Tile(Vector2I pos, TileDefinition definition)
+ public Tile(Vector2I pos, TileType type)
{
// Tile herda da classe Sprite2D.
// Por padrão, a posição do Sprite2D é no centro de sua textura.
@@ -29,7 +54,7 @@ public partial class Tile : Sprite2D
// Tiles começam invisíveis porque não foram vistos pelo jogador.
Visible = false;
Position = Grid.GridToWorld(pos);
- SetDefinition(definition);
+ Key = type;
}
/// <summary>
@@ -89,4 +114,22 @@ public partial class Tile : Sprite2D
IsWalkable = definition.IsWalkable;
IsTransparent = definition.IsTransparent;
}
+
+ public Dictionary<string, Variant> GetSaveData()
+ {
+ return new()
+ {
+ {"key", (int)Key},
+ {"is_explored", IsExplored}
+ };
+ }
+
+ public bool LoadSaveData(Dictionary<string, Variant> saveData)
+ {
+ // É o seguinte, não tenho tempo, não vou verificar se a entrada está correta.
+ Key = (TileType)(int)saveData["key"];
+
+ IsExplored = (bool)saveData["is_explored"];
+ return true;
+ }
}
diff --git a/scripts/Utils/ISaveable.cs b/scripts/Utils/ISaveable.cs
new file mode 100644
index 0000000..12f5784
--- /dev/null
+++ b/scripts/Utils/ISaveable.cs
@@ -0,0 +1,9 @@
+
+using Godot;
+
+public interface ISaveable
+{
+ public Godot.Collections.Dictionary<string, Variant> GetSaveData();
+
+ public bool LoadSaveData(Godot.Collections.Dictionary<string, Variant> saveData);
+} \ No newline at end of file
diff --git a/scripts/Utils/ISaveable.cs.uid b/scripts/Utils/ISaveable.cs.uid
new file mode 100644
index 0000000..6274214
--- /dev/null
+++ b/scripts/Utils/ISaveable.cs.uid
@@ -0,0 +1 @@
+uid://dx53repg15jv