From c4f5404211ef654944d5615e9055c714441f8234 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 9 Nov 2025 20:16:40 -0300 Subject: desgosto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Este commit marca minha desistência oficial do projeto. Minha preocupação em seguir um tutorial, adicionando minhas próprias features sem se importar com o design da aplicação como um todo resultou em um código maior que o necessário e difícil de manter. Toda vez que eu me forço a trabalhar no jogo, me sinto extremamente desmotivado e enojado. Os próximos commits serão para terminar o jogo para a apresentação. Eu planejo fazer um novo roguelike durante minhas férias da faculade, sem prazos, sem a limitação de usar todos os pilares da programação orientada a objetos e com planejamento minimamente decente. O projeto como um todo não foi um desperdício de tempo. Não só me ensinou o que não fazer, como também será um ótimo alimento para o copilot e outros LLMs. - Matheus Ferreira Guedes. --- assets/definitions/Items/uohhh.tres | 21 ++++++++ assets/definitions/actor/Player.tres | 2 +- project.godot | 1 + scripts/Entities/Actions/EscapeAction.cs | 76 ++++++++++++++++++++++++++- scripts/Entities/Actors/Actor.cs | 6 +++ scripts/Game.cs | 2 + scripts/GameManager.cs | 2 + scripts/InputHandling/MainGameInputHandler.cs | 2 +- scripts/Map/DungeonGenerator.cs | 62 ++++++++++++++++------ scripts/Utils/Stats.cs | 52 ++++++++++++++++++ scripts/Utils/Stats.cs.uid | 1 + 11 files changed, 207 insertions(+), 20 deletions(-) create mode 100644 assets/definitions/Items/uohhh.tres create mode 100644 scripts/Utils/Stats.cs create mode 100644 scripts/Utils/Stats.cs.uid diff --git a/assets/definitions/Items/uohhh.tres b/assets/definitions/Items/uohhh.tres new file mode 100644 index 0000000..c48c07a --- /dev/null +++ b/assets/definitions/Items/uohhh.tres @@ -0,0 +1,21 @@ +[gd_resource type="Resource" script_class="ItemResource" load_steps=6 format=3 uid="uid://otklq7k1tb45"] + +[ext_resource type="Script" uid="uid://bybli1lduvm3n" path="res://scripts/Entities/Items/ItemResource.cs" id="1_6qucm"] +[ext_resource type="Script" uid="uid://bslt4pbvwvsj1" path="res://scripts/Magic/HealEffect.cs" id="1_uvkms"] +[ext_resource type="Script" uid="uid://b72fwkwul1wet" path="res://scripts/Entities/Items/SpellEffectItemActivation.cs" id="2_i5wwe"] + +[sub_resource type="Resource" id="Resource_idx7p"] +script = ExtResource("1_uvkms") +Healing = 89 +metadata/_custom_type_script = "uid://bslt4pbvwvsj1" + +[sub_resource type="Resource" id="Resource_v7ab0"] +script = ExtResource("2_i5wwe") +effect = SubResource("Resource_idx7p") +metadata/_custom_type_script = "uid://b72fwkwul1wet" + +[resource] +script = ExtResource("1_6qucm") +DisplayName = "ojo" +Activation = SubResource("Resource_v7ab0") +metadata/_custom_type_script = "uid://bybli1lduvm3n" diff --git a/assets/definitions/actor/Player.tres b/assets/definitions/actor/Player.tres index 8a4e2c1..58062c3 100644 --- a/assets/definitions/actor/Player.tres +++ b/assets/definitions/actor/Player.tres @@ -11,7 +11,7 @@ deathTexture = ExtResource("1_m72ac") Hp = 36 Mp = 12 Atk = 8 -Def = 10 +Def = 3 Men = 3 name = "Jogador" texture = ExtResource("3_m72ac") diff --git a/project.godot b/project.godot index 5ee82d7..1cd62bd 100644 --- a/project.godot +++ b/project.godot @@ -20,6 +20,7 @@ config/icon="res://icon.svg" signalBus="*res://scripts/Utils/SignalBus.cs" MessageLogData="*res://scripts/Utils/MessageLogData.cs" +Stats="*res://scripts/Utils/Stats.cs" [display] diff --git a/scripts/Entities/Actions/EscapeAction.cs b/scripts/Entities/Actions/EscapeAction.cs index f0b1ed5..1b3aae2 100644 --- a/scripts/Entities/Actions/EscapeAction.cs +++ b/scripts/Entities/Actions/EscapeAction.cs @@ -1,13 +1,85 @@ +using System; +using Godot; +using Godot.Collections; +using Microsoft.VisualBasic; using TheLegendOfGustav.Entities.Actors; using TheLegendOfGustav.Utils; namespace TheLegendOfGustav.Entities.Actions; -public partial class EscapeAction(Actor actor) : Action(actor) +public partial class EscapeAction(Actor actor, bool should_save = false) : Action(actor) { + private bool should_save = should_save; public override bool Perform() { - Actor.MapData.SaveGame(); + if (should_save) { + Actor.MapData.SaveGame(); + } else { + // game over + bool hasLeaderboardFile = FileAccess.FileExists("user://placar.json"); + if (hasLeaderboardFile) { + using var leaderboardFile = FileAccess.Open("user://placar.json", FileAccess.ModeFlags.ReadWrite); + string boardString = leaderboardFile.GetLine(); + + Dictionary leaderBoardData; + + try { + var parseResult = Json.ParseString(boardString); + if (parseResult.VariantType == Variant.Type.Nil) { + throw new Exception(); + } + leaderBoardData = (Dictionary)parseResult; + } catch (Exception) + { + leaderboardFile.Resize(0); + leaderboardFile.Seek(0); + + leaderBoardData = new() + { + {"placar", new Array>() {Stats.Instance.Serialize()}} + }; + boardString = Json.Stringify(leaderBoardData); + + leaderboardFile.StoreLine(boardString); + + SignalBus.Instance.EmitSignal(SignalBus.SignalName.EscapeRequested); + return false; + } + + Array> players = (Array>)leaderBoardData["placar"]; + + players.Add(Stats.Instance.Serialize()); + + for (int i = 0; i < players.Count; i++) { + for (int j = 0; j < players.Count - 1 - i; j++) { + if ((int)players[j]["andar_mais_fundo"] < (int)players[j + 1]["andar_mais_fundo"]) { + Dictionary tmp = players[j]; + players[j] = players[j + 1]; + players[j + 1] = tmp; + } + } + } + + if (players.Count > 10) { + players = players.GetSliceRange(0, 10); + } + + leaderBoardData["placar"] = players; + + leaderboardFile.Resize(0); + leaderboardFile.Seek(0); + leaderboardFile.StoreLine(Json.Stringify(leaderBoardData)); + } else { + using var leaderboardFile = FileAccess.Open("user://placar.json", FileAccess.ModeFlags.Write); + Dictionary leaderBoardData = new() + { + {"placar", new Array>() {Stats.Instance.Serialize()}} + }; + string boardString = Json.Stringify(leaderBoardData); + + leaderboardFile.StoreLine(boardString); + } + } SignalBus.Instance.EmitSignal(SignalBus.SignalName.EscapeRequested); return false; } diff --git a/scripts/Entities/Actors/Actor.cs b/scripts/Entities/Actors/Actor.cs index c68cc2b..e6b8867 100644 --- a/scripts/Entities/Actors/Actor.cs +++ b/scripts/Entities/Actors/Actor.cs @@ -101,6 +101,9 @@ public partial class Actor : Entity, ISaveable get => hp; set { + if (MapData != null && MapData.Player == this && hp > value) { + Stats.Instance.DamageTaken += (hp - value); + } // Esta propriedade impede que o HP seja maior que o máximo. hp = int.Clamp(value, 0, MaxHp); EmitSignal(SignalName.HealthChanged, Hp, MaxHp); @@ -280,6 +283,9 @@ public partial class Actor : Entity, ISaveable else { deathMessage = $"{DisplayName} morreu!"; + if (!inLoading) { + Stats.Instance.EnemiesKilled++; + } } diff --git a/scripts/Game.cs b/scripts/Game.cs index e55b937..cc86928 100644 --- a/scripts/Game.cs +++ b/scripts/Game.cs @@ -90,6 +90,8 @@ public partial class Game : Node player.AddChild(camera); + Stats.Instance.PlayerName = player.DisplayName; + if (!map.LoadGame(player)) { return false; diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs index 5898744..df93954 100644 --- a/scripts/GameManager.cs +++ b/scripts/GameManager.cs @@ -42,6 +42,7 @@ public partial class GameManager : Node private void LoadMainMenu() { MainMenu menu = (MainMenu)SwitchToScene(mainMenuScene); + Stats.Instance.Clear(); menu.GameRequest += OnGameRequest; } @@ -72,6 +73,7 @@ public partial class GameManager : Node private void OnNameSelect(string name) { + Stats.Instance.PlayerName = name; NewGame(name); } diff --git a/scripts/InputHandling/MainGameInputHandler.cs b/scripts/InputHandling/MainGameInputHandler.cs index a18bd73..d1b5818 100644 --- a/scripts/InputHandling/MainGameInputHandler.cs +++ b/scripts/InputHandling/MainGameInputHandler.cs @@ -60,7 +60,7 @@ public partial class MainGameInputHandler : BaseInputHandler if (Input.IsActionJustPressed("quit")) { - action = new EscapeAction(player); + action = new EscapeAction(player, true); } if (Input.IsActionJustPressed("descend")) diff --git a/scripts/Map/DungeonGenerator.cs b/scripts/Map/DungeonGenerator.cs index 779675f..7f57353 100644 --- a/scripts/Map/DungeonGenerator.cs +++ b/scripts/Map/DungeonGenerator.cs @@ -3,6 +3,9 @@ using TheLegendOfGustav.Entities.Actors; using TheLegendOfGustav.Entities; using TheLegendOfGustav.Entities.Items; using System; +using Godot.Collections; +using System.Diagnostics.Metrics; +using System.Numerics; namespace TheLegendOfGustav.Map; @@ -14,6 +17,27 @@ public partial class DungeonGenerator : Node { #region Fields /// + /// Chave: Andar mínimo + /// Valor: Número de máximo de monstros por sala + /// + private static readonly Dictionary maxMonstersByFloor = new() + { + {1, 2}, + {4, 3}, + {6, 4}, + {10, 8} + }; + /// + /// Chave: Andar mínimo + /// Valor: Número de máximo de itens por sala + /// + private static readonly Dictionary maxItemsByFloor = new() + { + {1, 1}, + {4, 2}, + {6, 3}, + }; + /// /// Coleção de todos os inimigos que o gerador tem acesso. /// private static readonly Godot.Collections.Array enemies = [ @@ -47,24 +71,30 @@ public partial class DungeonGenerator : Node /// [ExportCategory("RNG")] private RandomNumberGenerator rng = new(); - - /// - /// Quantidade máxima de inimigos por sala. - /// - [ExportCategory("Monster RNG")] - [Export] - private int maxMonstersPerRoom = 2; - - /// - /// Quantidade máxima de itens por sala. - /// - [ExportCategory("Loot RNG")] - [Export] - private int maxItemsPerRoom = 2; #endregion #region Methods + private int GetMaxIValueForFloor(Dictionary valueTable, int currentFloor) { + int currentValue = 0; + + int? key = null; + + foreach (int theKey in valueTable.Keys) { + if (theKey > currentFloor) { + break; + } else { + key = theKey; + } + } + + if (key.HasValue) { + currentValue = valueTable[key.Value]; + } + + return currentValue; + } + /// /// Gera um andar da masmorra. /// Inimigos são colocados conforme configurações. @@ -229,9 +259,9 @@ public partial class DungeonGenerator : Node private void PlaceEntities(MapData data, Rect2I room) { // Define quantos monstros serão colocados na sala - int monsterAmount = rng.RandiRange(0, maxMonstersPerRoom); + int monsterAmount = rng.RandiRange(0, GetMaxIValueForFloor(maxMonstersByFloor, data.CurrentFloor)); // Define quantos itens serão colocados na sala. - int itemAmount = rng.RandiRange(0, maxItemsPerRoom); + int itemAmount = rng.RandiRange(0, GetMaxIValueForFloor(maxItemsByFloor, data.CurrentFloor)); for (int i = 0; i < monsterAmount; i++) { diff --git a/scripts/Utils/Stats.cs b/scripts/Utils/Stats.cs new file mode 100644 index 0000000..d3c4aa3 --- /dev/null +++ b/scripts/Utils/Stats.cs @@ -0,0 +1,52 @@ +using Godot; +using Godot.Collections; +using TheLegendOfGustav.Utils; + +namespace TheLegendOfGustav.Utils; + +public partial class Stats : Node +{ + public static Stats Instance { get; set; } + + public string PlayerName { get; set; } + public int MaxFloor { get; set; } = 0; + + public int EnemiesKilled { get; set; } = 0; + + public int DamageTaken { get; set; } = 0; + + public override void _Ready() + { + base._Ready(); + Instance = this; + + SignalBus.Instance.DungeonFloorChanged += OnFloorChange; + } + + public void Clear() + { + PlayerName = ""; + MaxFloor = 0; + EnemiesKilled = 0; + DamageTaken = 0; + } + + void OnFloorChange(int floor) + { + if (floor > MaxFloor) + { + MaxFloor = floor; + } + } + + public Dictionary Serialize() + { + return new() + { + {"jogador", PlayerName}, + {"andar_mais_fundo", MaxFloor}, + {"inimigos_mortos", EnemiesKilled}, + {"dano_tomado", DamageTaken} + }; + } +} \ No newline at end of file diff --git a/scripts/Utils/Stats.cs.uid b/scripts/Utils/Stats.cs.uid new file mode 100644 index 0000000..a93778d --- /dev/null +++ b/scripts/Utils/Stats.cs.uid @@ -0,0 +1 @@ +uid://eg7t08o6wpxb -- cgit v1.2.3 From 0f3c49b7743f7c7fabf41fdf4cb3ffe1a4ac3bf4 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 10 Nov 2025 12:56:00 -0300 Subject: Placar. --- scenes/GUI/Leaderboard.cs | 62 ++++++++++++++++++++++++++++++++++++++ scenes/GUI/Leaderboard.cs.uid | 1 + scenes/GUI/Leaderboard.tscn | 51 +++++++++++++++++++++++++++++++ scenes/GUI/leaderboard_item.tscn | 35 +++++++++++++++++++++ scenes/GUI/main_menu.tscn | 15 ++++++++- scripts/GUI/LeaderboardItem.cs | 41 +++++++++++++++++++++++++ scripts/GUI/LeaderboardItem.cs.uid | 1 + scripts/GUI/MainMenu.cs | 12 ++++++++ scripts/GameManager.cs | 8 +++++ 9 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 scenes/GUI/Leaderboard.cs create mode 100644 scenes/GUI/Leaderboard.cs.uid create mode 100644 scenes/GUI/Leaderboard.tscn create mode 100644 scenes/GUI/leaderboard_item.tscn create mode 100644 scripts/GUI/LeaderboardItem.cs create mode 100644 scripts/GUI/LeaderboardItem.cs.uid diff --git a/scenes/GUI/Leaderboard.cs b/scenes/GUI/Leaderboard.cs new file mode 100644 index 0000000..cf126c9 --- /dev/null +++ b/scenes/GUI/Leaderboard.cs @@ -0,0 +1,62 @@ +using System; +using Godot; +using Godot.Collections; +using TheLegendOfGustav.Utils; + +namespace TheLegendOfGustav.GUI; +public partial class Leaderboard : Control +{ + [Signal] + public delegate void MenuRequestedEventHandler(); + private static readonly PackedScene LeaderboardItemScene = GD.Load("res://scenes/GUI/leaderboard_item.tscn"); + + private VBoxContainer leaderboard; + private Button BackButton; + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + leaderboard = GetNode("VBoxContainer/VBoxContainer"); + BackButton = GetNode