diff --git a/Scripts/PlayerSystem/Item.cs b/Scripts/PlayerSystem/Item.cs new file mode 100644 index 0000000..9168b30 --- /dev/null +++ b/Scripts/PlayerSystem/Item.cs @@ -0,0 +1,29 @@ +// Item.cs +namespace TextRPG.PlayerSystem +{ + public class Item + { + public string Name { get; set; } + public string Description { get; set; } + public int Value { get; set; } // e.g., healing power, sell price + public string Type { get; set; } // "consumable", "weapon", "key", etc. + + public Item(string name, string description, int value = 0, string type = "misc") + { + Name = name; + Description = description; + Value = value; + Type = type; + } + + // Example: use effect (could be expanded with a delegate or virtual method) + public virtual void Use(Player player) + { + if (Type == "consumable") + { + player.Heal(Value); + // In a full implementation, you would also remove the item after use + } + } + } +} \ No newline at end of file diff --git a/Scripts/PlayerSystem/Item.cs.uid b/Scripts/PlayerSystem/Item.cs.uid new file mode 100644 index 0000000..682dae9 --- /dev/null +++ b/Scripts/PlayerSystem/Item.cs.uid @@ -0,0 +1 @@ +uid://dvyff657dfv87 diff --git a/Scripts/PlayerSystem/Player.cs b/Scripts/PlayerSystem/Player.cs new file mode 100644 index 0000000..801699d --- /dev/null +++ b/Scripts/PlayerSystem/Player.cs @@ -0,0 +1,121 @@ +// Player.cs - Complete working version +using Godot; +using System; +using System.Collections.Generic; + +namespace TextRPG.PlayerSystem +{ + public partial class Player : Node + { + // ---------- Singleton Pattern ---------- + private static Player _instance; + public static Player Instance => _instance; + + // ---------- Signals ---------- + [Signal] + public delegate void HealthChangedEventHandler(int currentHealth, int maxHealth); + [Signal] + public delegate void PlayerDiedEventHandler(); + [Signal] + public delegate void InventoryChangedEventHandler(); + + // ---------- Attributes ---------- + public int MaxHealth { get; set; } = 100; + private int _currentHealth; + public int CurrentHealth + { + get => _currentHealth; + set + { + int newHealth = Math.Clamp(value, 0, MaxHealth); + if (_currentHealth != newHealth) + { + _currentHealth = newHealth; + EmitSignal(SignalName.HealthChanged, _currentHealth, MaxHealth); + if (_currentHealth <= 0) + EmitSignal(SignalName.PlayerDied); + } + } + } + + public int Strength { get; set; } = 10; + public int Dexterity { get; set; } = 10; + public int Intelligence { get; set; } = 10; + + // ---------- Inventory ---------- + public List Inventory { get; private set; } = new List(); + + // ---------- Lifecycle ---------- + public override void _EnterTree() + { + if (_instance != null) + { + GD.Print("Duplicate Player instance detected. Removing."); + QueueFree(); + } + else + { + _instance = this; + GD.Print("Player singleton initialized."); + } + } + + public override void _Ready() + { + _currentHealth = MaxHealth; + } + + // ---------- Public Methods ---------- + public void TakeDamage(int amount) + { + CurrentHealth -= amount; + } + + public void Heal(int amount) + { + CurrentHealth += amount; + } + + public void AddItem(Item item) + { + Inventory.Add(item); + EmitSignal(SignalName.InventoryChanged); + } + + public bool RemoveItem(Item item) + { + bool removed = Inventory.Remove(item); + if (removed) + EmitSignal(SignalName.InventoryChanged); + return removed; + } + + public bool HasItem(string itemName) + { + return Inventory.Exists(i => i.Name.Equals(itemName, StringComparison.OrdinalIgnoreCase)); + } + + public Item GetItem(string itemName) + { + return Inventory.Find(i => i.Name.Equals(itemName, StringComparison.OrdinalIgnoreCase)); + } + + public string GetStatusString() + { + return $"Health: {CurrentHealth}/{MaxHealth}\n" + + $"Strength: {Strength} Dexterity: {Dexterity} Intelligence: {Intelligence}\n" + + $"Inventory: {Inventory.Count} items"; + } + + public string GetInventoryString() + { + if (Inventory.Count == 0) + return "Your inventory is empty."; + + var lines = new List { "=== Inventory ===" }; + foreach (var item in Inventory) + lines.Add($"- {item.Name}: {item.Description}"); + return string.Join("\n", lines); + } + } +} \ No newline at end of file diff --git a/Scripts/PlayerSystem/Player.cs.uid b/Scripts/PlayerSystem/Player.cs.uid new file mode 100644 index 0000000..39fb27a --- /dev/null +++ b/Scripts/PlayerSystem/Player.cs.uid @@ -0,0 +1 @@ +uid://cx4q1u5whnn7m diff --git a/TextRPG.csproj b/TextRPG.csproj index e457872..42fc5bc 100644 --- a/TextRPG.csproj +++ b/TextRPG.csproj @@ -1,4 +1,4 @@ - + net8.0 net9.0 diff --git a/TextRPG.csproj.old b/TextRPG.csproj.old new file mode 100644 index 0000000..e457872 --- /dev/null +++ b/TextRPG.csproj.old @@ -0,0 +1,7 @@ + + + net8.0 + net9.0 + true + + \ No newline at end of file diff --git a/commands/ConsoleManager.cs b/commands/ConsoleManager.cs index 18a2724..cb83d7f 100644 --- a/commands/ConsoleManager.cs +++ b/commands/ConsoleManager.cs @@ -15,6 +15,11 @@ namespace TextRPG.Commands RegisterCommand(new HelpCommand()); RegisterCommand(new ClearCommand()); RegisterCommand(new TimeCommand()); + RegisterCommand(new StatusCommand()); + RegisterCommand(new InventoryCommand()); + RegisterCommand(new TakeCommand()); + RegisterCommand(new DropCommand()); + RegisterCommand(new HealCommand()); } diff --git a/commands/DropCommand.cs b/commands/DropCommand.cs new file mode 100644 index 0000000..e9ca516 --- /dev/null +++ b/commands/DropCommand.cs @@ -0,0 +1,33 @@ +// DropCommand.cs +using System; +using TextRPG.PlayerSystem; + +namespace TextRPG.Commands +{ + public class DropCommand : Command + { + public override string Name => "drop"; + public override string Description => "Removes an item from your inventory."; + public override string Usage => "drop "; + + public override void Execute(string[] args, Action outputCallback) + { + if (args.Length == 0) + { + outputCallback($"Usage: {Usage}"); + return; + } + + string itemName = string.Join(" ", args); + var item = Player.Instance.GetItem(itemName); + if (item == null) + { + outputCallback($"You don't have '{itemName}'."); + return; + } + + Player.Instance.RemoveItem(item); + outputCallback($"You dropped: {itemName}."); + } + } +} \ No newline at end of file diff --git a/commands/DropCommand.cs.uid b/commands/DropCommand.cs.uid new file mode 100644 index 0000000..5876820 --- /dev/null +++ b/commands/DropCommand.cs.uid @@ -0,0 +1 @@ +uid://bfa36j6ca7bj6 diff --git a/commands/HealCommand.cs b/commands/HealCommand.cs new file mode 100644 index 0000000..ac0b82d --- /dev/null +++ b/commands/HealCommand.cs @@ -0,0 +1,26 @@ +// HealCommand.cs +using System; +using TextRPG.PlayerSystem; + +namespace TextRPG.Commands +{ + public class HealCommand : Command + { + public override string Name => "heal"; + public override string Description => "Heals the player by a given amount (max 50)."; + public override string Usage => "heal "; + + public override void Execute(string[] args, Action outputCallback) + { + if (args.Length == 0 || !int.TryParse(args[0], out int amount)) + { + outputCallback($"Usage: {Usage}"); + return; + } + + amount = Math.Min(amount, 50); + Player.Instance.Heal(amount); + outputCallback($"You healed {amount} HP. Current health: {Player.Instance.CurrentHealth}/{Player.Instance.MaxHealth}"); + } + } +} \ No newline at end of file diff --git a/commands/HealCommand.cs.uid b/commands/HealCommand.cs.uid new file mode 100644 index 0000000..a177d0f --- /dev/null +++ b/commands/HealCommand.cs.uid @@ -0,0 +1 @@ +uid://crubvxtr4u1ya diff --git a/commands/InventoryCommand.cs b/commands/InventoryCommand.cs new file mode 100644 index 0000000..d3517ae --- /dev/null +++ b/commands/InventoryCommand.cs @@ -0,0 +1,18 @@ +// InventoryCommand.cs +using System; +using TextRPG.PlayerSystem; + +namespace TextRPG.Commands +{ + public class InventoryCommand : Command + { + public override string Name => "inventory"; + public override string Description => "Lists all items in your inventory."; + public override string Usage => "inventory"; + + public override void Execute(string[] args, Action outputCallback) + { + outputCallback(Player.Instance.GetInventoryString()); + } + } +} \ No newline at end of file diff --git a/commands/InventoryCommand.cs.uid b/commands/InventoryCommand.cs.uid new file mode 100644 index 0000000..5e39295 --- /dev/null +++ b/commands/InventoryCommand.cs.uid @@ -0,0 +1 @@ +uid://c2m6vnugsmxgg diff --git a/commands/StatusCommand.cs b/commands/StatusCommand.cs new file mode 100644 index 0000000..f59c3d8 --- /dev/null +++ b/commands/StatusCommand.cs @@ -0,0 +1,24 @@ +using System; +using TextRPG.PlayerSystem; + +namespace TextRPG.Commands +{ + public class StatusCommand : Command + { + public override string Name => "status"; + public override string Description => "Shows player status (health, attributes, inventory count)."; + public override string Usage => "status"; + + public override void Execute(string[] args, Action outputCallback) + { + // Use the singleton instance directly + var player = Player.Instance; + if (player == null) + { + outputCallback("Player system not initialized."); + return; + } + outputCallback(player.GetStatusString()); + } + } +} \ No newline at end of file diff --git a/commands/StatusCommand.cs.uid b/commands/StatusCommand.cs.uid new file mode 100644 index 0000000..9330afc --- /dev/null +++ b/commands/StatusCommand.cs.uid @@ -0,0 +1 @@ +uid://c2ig5hu00nyql diff --git a/commands/TakeCommand.cs b/commands/TakeCommand.cs new file mode 100644 index 0000000..4daaaf3 --- /dev/null +++ b/commands/TakeCommand.cs @@ -0,0 +1,28 @@ +// TakeCommand.cs +using System; +using TextRPG.PlayerSystem; + +namespace TextRPG.Commands +{ + public class TakeCommand : Command + { + public override string Name => "take"; + public override string Description => "Picks up an item from the current location."; + public override string Usage => "take "; + + public override void Execute(string[] args, Action outputCallback) + { + if (args.Length == 0) + { + outputCallback($"Usage: {Usage}"); + return; + } + + string itemName = string.Join(" ", args); + // For demo, create a predefined item. In a real game, you'd fetch from the current room/zone. + var item = new Item(itemName, "A mysterious object.", 10, "misc"); + Player.Instance.AddItem(item); + outputCallback($"You picked up: {itemName}."); + } + } +} \ No newline at end of file diff --git a/commands/TakeCommand.cs.uid b/commands/TakeCommand.cs.uid new file mode 100644 index 0000000..00f035d --- /dev/null +++ b/commands/TakeCommand.cs.uid @@ -0,0 +1 @@ +uid://b6trmclox3caa diff --git a/project.godot b/project.godot index 9d8674f..c01acdc 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,10 @@ run/main_scene="uid://cpbeaf3bpe42v" config/features=PackedStringArray("4.6", "C#", "Forward Plus") config/icon="res://icon.svg" +[autoload] + +Player="*uid://cx4q1u5whnn7m" + [dotnet] project/assembly_name="TextRPG" diff --git a/scenes/ConsoleUI.cs b/scenes/ConsoleUI.cs index 0075760..9b2e608 100644 --- a/scenes/ConsoleUI.cs +++ b/scenes/ConsoleUI.cs @@ -1,10 +1,14 @@ using Godot; using System; using System.Collections.Generic; -using TextRPG.Commands; // Add this to access ConsoleManager +using TextRPG.Commands; +using TextRPG.PlayerSystem; + + public partial class ConsoleUI : CanvasLayer { + private Player player; // UI Elements private RichTextLabel consoleOutput; private LineEdit consoleInput; @@ -58,6 +62,31 @@ public partial class ConsoleUI : CanvasLayer // Display welcome message DisplayWelcomeMessage(); + player = GetNode("/root/Player"); + if (player != null) + { + player.HealthChanged += OnPlayerHealthChanged; + player.PlayerDied += OnPlayerDied; + player.InventoryChanged += OnInventoryChanged; + } + + } + + private void OnPlayerHealthChanged(int current, int max) + { + AddToConsole($"[color=orange]Health changed: {current}/{max}[/color]"); + } + + private void OnPlayerDied() + { + AddToConsole("[color=red]You have died... Game Over.[/color]"); + // Optionally disable input or reload + } + + private void OnInventoryChanged() + { + // Optional: show a subtle message or nothing – you decide + // AddToConsole("[dim]Inventory updated.[/dim]"); } private void SetupConsole()