From f4ed469fc9eaeebf39093fbf6601581cc10c6e2f Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 26 Oct 2025 20:02:15 -0300 Subject: feat:save AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O vazio dentro de mim é como uma xícara de café esquecida no canto da mesa. --- scripts/Map/DungeonGenerator.cs | 2 +- scripts/Map/Map.cs | 18 ++++++ scripts/Map/MapData.cs | 120 ++++++++++++++++++++++++++++++++++++++-- scripts/Map/Tile.cs | 49 +++++++++++++++- 4 files changed, 181 insertions(+), 8 deletions(-) (limited to 'scripts/Map') 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; } /// 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. /// -public partial class MapData : RefCounted +public partial class MapData : RefCounted, ISaveable { #region Fields - 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"); /// /// 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 /// 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 GetSaveData() + { + Array> serializedTiles = []; + Array> serializedItemEntities = []; + Array> 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 saveData) + { + Width = (int)saveData["width"]; + Height = (int)saveData["height"]; + + SetupTiles(); + + Array> serializedTiles = (Array>)saveData["tiles"]; + Array> serializedItemEntities = (Array>)saveData["items"]; + Array> serializedEnemies = (Array>)saveData["enemies"]; + + for (int i = 0; i < serializedTiles.Count; i++) + { + if (!Tiles[i].LoadSaveData(serializedTiles[i])) + { + return false; + } + } + SetupPathfinding(); + if (!Player.LoadSaveData((Dictionary)saveData["player"])) + { + return false; + } + + Player.MapData = this; + + foreach(Dictionary enemy in serializedEnemies) + { + Enemy en = new(Vector2I.Zero, this); + if (!en.LoadSaveData(enemy)) + { + return false; + } + + InsertEntity(en); + } + + foreach(Dictionary 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)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 +} + /// /// 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. /// -public partial class Tile : Sprite2D +public partial class Tile : Sprite2D, ISaveable { + private static readonly Godot.Collections.Dictionary Types = new() + { + {TileType.WALL, GD.Load("res://assets/definitions/tiles/wall.tres")}, + {TileType.FLOOR, GD.Load("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 /// 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; } /// @@ -89,4 +114,22 @@ public partial class Tile : Sprite2D IsWalkable = definition.IsWalkable; IsTransparent = definition.IsTransparent; } + + public Dictionary GetSaveData() + { + return new() + { + {"key", (int)Key}, + {"is_explored", IsExplored} + }; + } + + public bool LoadSaveData(Dictionary 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; + } } -- cgit v1.2.3