diff --git a/README.md b/README.md index d9ca412..fc5db91 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,32 @@ A game project built with Godot 4.5 and C#. This project uses the following custom libraries: - **EinSoftworks.Utilities** - Math and utility functions -- **EinSoftworks.Input** - Input management system - **EinSoftworks.Events** - Event system for game communication +- **EinSoftworks.Input** - Input management system +- **EinSoftworks.StateManagement** - State machine system ## Project Structure ``` voider/ ├── Scenes/ # Game scenes (.tscn files) +│ ├── Core/ # Core game scenes +│ │ └── Main.tscn +│ ├── Player/ # Player scenes +│ ├── Enemies/ # Enemy scenes +│ ├── UI/ # UI scenes +│ └── Testing/ # Test scenes +│ └── LibraryTest.tscn ├── Scripts/ # C# scripts -├── Resources/ # Game resources -│ ├── Textures/ # Images and sprites -│ ├── Audio/ # Sound effects and music -│ └── Fonts/ # Font files -├── Prefabs/ # Reusable scene prefabs -└── UI/ # User interface scenes +│ ├── Core/ # Core game systems +│ │ ├── Main.cs +│ │ └── EventManager.cs +│ ├── Player/ # Player-related scripts +│ ├── Enemies/ # Enemy scripts +│ ├── UI/ # UI scripts +│ └── Testing/ # Test and debug scripts +│ └── LibraryTest.cs +└── Docs/ # Documentation ``` ## Development @@ -33,6 +44,10 @@ dotnet build ### Running Open the project in Godot 4.5 and press F5 to run. +### Testing Libraries +Run the `Scenes/Testing/LibraryTest.tscn` scene (F6) to test all integrated libraries. +See `Docs/LIBRARY_TEST.md` for details. + ## License TBD diff --git a/Scenes/Core/Main.tscn b/Scenes/Core/Main.tscn new file mode 100644 index 0000000..960f029 --- /dev/null +++ b/Scenes/Core/Main.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bvx8qw3yqn5yk"] + +[ext_resource type="Script" uid="uid://emch0q5mxf8k" path="res://Scripts/Core/Main.cs" id="1_1"] + +[node name="Main" type="Node2D"] +script = ExtResource("1_1") diff --git a/Scenes/LibraryTest.tscn b/Scenes/LibraryTest.tscn new file mode 100644 index 0000000..e69de29 diff --git a/Scenes/Main.tscn b/Scenes/Main.tscn index 665ec1e..e69de29 100644 --- a/Scenes/Main.tscn +++ b/Scenes/Main.tscn @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bvx8qw3yqn5yk"] - -[ext_resource type="Script" uid="uid://emch0q5mxf8k" path="res://Scripts/Main.cs" id="1_1"] - -[node name="Main" type="Node2D"] -script = ExtResource("1_1") diff --git a/Scenes/Testing/LibraryTest.tscn b/Scenes/Testing/LibraryTest.tscn new file mode 100644 index 0000000..141d380 --- /dev/null +++ b/Scenes/Testing/LibraryTest.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://c8yv7qm3xwxqj"] + +[ext_resource type="Script" path="res://Scripts/Testing/LibraryTest.cs" id="1_library_test"] + +[node name="LibraryTest" type="Node"] +script = ExtResource("1_library_test") diff --git a/Scripts/Main.cs b/Scripts/Core/Main.cs similarity index 100% rename from Scripts/Main.cs rename to Scripts/Core/Main.cs diff --git a/Scripts/Main.cs.uid b/Scripts/Core/Main.cs.uid similarity index 100% rename from Scripts/Main.cs.uid rename to Scripts/Core/Main.cs.uid diff --git a/Scripts/Testing/LibraryTest.cs b/Scripts/Testing/LibraryTest.cs new file mode 100644 index 0000000..95d3d80 --- /dev/null +++ b/Scripts/Testing/LibraryTest.cs @@ -0,0 +1,262 @@ +using System; +using EinSoftworks.Events; +using EinSoftworks.Input; +using EinSoftworks.StateManagement; +using EinSoftworks.Utilities; +using Godot; + +namespace Voider; + +public partial class LibraryTest : Node +{ + private StateMachine _stateMachine; + private HierarchicalStateMachine _hierarchicalSM; + private EventBus _eventBus; + private int _frameCount = 0; + private IdlePlayerState _idleState; + private RunningPlayerState _runningState; + private JumpingPlayerState _jumpingState; + + public override void _Ready() + { + GD.Print("=== EinSoftworks Library Integration Test ===\n"); + + TestUtilities(); + TestEvents(); + TestInput(); + TestStateMachine(); + TestHierarchicalStateMachine(); + + GD.Print("\n=== All Tests Initialized Successfully ==="); + GD.Print("Watch console for runtime behavior...\n"); + } + + public override void _Process(double delta) + { + _frameCount++; + + // Update state machines + _stateMachine?.Update((float)delta); + _hierarchicalSM?.Update((float)delta); + + // Test transitions every 60 frames + if (_frameCount == 60) + { + GD.Print("\n[Frame 60] Testing state transitions..."); + _stateMachine?.ChangeState(_runningState); + } + else if (_frameCount == 120) + { + GD.Print("\n[Frame 120] Testing event-triggered transition..."); + _stateMachine?.TriggerEvent("jump"); + } + else if (_frameCount == 180) + { + GD.Print("\n[Frame 180] Testing hierarchical state change..."); + _hierarchicalSM?.ChangeState("Combat"); + } + } + + private void TestUtilities() + { + GD.Print("--- Testing EinSoftworks.Utilities ---"); + + // Test MathUtils + float angle = 90f; + float radians = MathUtils.DegreesToRadians(angle); + GD.Print($"✓ DegreesToRadians: {angle}° = {radians} rad"); + + float backToDegrees = MathUtils.RadiansToDegrees(radians); + GD.Print($"✓ RadiansToDegrees: {radians} rad = {backToDegrees}°"); + + float value = 1.5f; + float clamped = MathUtils.Clamp01(value); + GD.Print($"✓ Clamp01: {value} clamped to {clamped}"); + + bool approx = MathUtils.Approximately(0.1f + 0.2f, 0.3f); + GD.Print($"✓ Approximately: 0.1 + 0.2 ≈ 0.3 = {approx}"); + + GD.Print(""); + } + + private void TestEvents() + { + GD.Print("--- Testing EinSoftworks.Events ---"); + + _eventBus = new EventBus(); + + // Subscribe to events + _eventBus.Subscribe(OnTestEvent); + GD.Print($"✓ EventBus created with {_eventBus.SubscriberCount} subscriber"); + + // Publish an event + _eventBus.Publish(new TestEvent("Initialization", 42)); + + GD.Print(""); + } + + private void TestInput() + { + GD.Print("--- Testing EinSoftworks.Input ---"); + + // Create input contexts + var gameplayContext = new InputContext( + "Gameplay", + 10, + InputContext.ContextMode.AllowList, + false + ); + gameplayContext.AllowAction("move_left"); + gameplayContext.AllowAction("move_right"); + gameplayContext.AllowAction("jump"); + + var menuContext = new InputContext("Menu", 20, InputContext.ContextMode.BlockList, true); + menuContext.BlockAction("move_left"); + menuContext.BlockAction("move_right"); + + GD.Print( + $"✓ Created InputContext: {gameplayContext.Name} (Priority: {gameplayContext.Priority})" + ); + GD.Print($"✓ Created InputContext: {menuContext.Name} (Priority: {menuContext.Priority})"); + GD.Print(" (InputManager is a Node singleton - add to scene tree for full functionality)"); + + // Test InputBuffer + var inputBuffer = new InputBuffer(0.15f); + inputBuffer.BufferInput("jump"); + bool hasJump = inputBuffer.IsInputBuffered("jump"); + GD.Print($"✓ InputBuffer: Buffered 'jump' action, has buffered = {hasJump}"); + + GD.Print(""); + } + + private void TestStateMachine() + { + GD.Print("--- Testing EinSoftworks.StateManagement (Basic) ---"); + + _stateMachine = new StateMachine(); + + // Create states + _idleState = new IdlePlayerState(); + _runningState = new RunningPlayerState(); + _jumpingState = new JumpingPlayerState(); + + // Subscribe to state changes + _stateMachine.StateChanged += (from, to) => + { + GD.Print( + $" → State changed: {from?.GetType().Name ?? "null"} → {to?.GetType().Name ?? "null"}" + ); + }; + + // Add transitions + _stateMachine.AddTransition(_idleState, _runningState, () => false); // Won't trigger automatically + _stateMachine.AddTransition(_runningState, _jumpingState, "jump"); + + // Set initial state + _stateMachine.ChangeState(_idleState); + + GD.Print( + $"✓ StateMachine created with initial state: {_stateMachine.CurrentState?.GetType().Name}" + ); + GD.Print(""); + } + + private void TestHierarchicalStateMachine() + { + GD.Print("--- Testing EinSoftworks.StateManagement (Hierarchical) ---"); + + _hierarchicalSM = new HierarchicalStateMachine(); + + // Create parent states + var exploration = "Exploration"; + var combat = "Combat"; + + // Create sub-state machine for combat + var combatSubStates = new StateMachine(); + combatSubStates.ChangeState("Attacking"); + + _hierarchicalSM.AddSubStateMachine(combat, combatSubStates); + + // Subscribe to state changes + _hierarchicalSM.StateChanged += (from, to) => + { + GD.Print($" → Hierarchical state changed: {from ?? "null"} → {to ?? "null"}"); + }; + + // Set initial state + _hierarchicalSM.ChangeState(exploration); + + GD.Print($"✓ HierarchicalStateMachine created with state: {_hierarchicalSM.CurrentState}"); + GD.Print(""); + } + + private void OnTestEvent(TestEvent evt) + { + GD.Print($" → Event received: {evt.Message} (Value: {evt.Value})"); + } + + public override void _ExitTree() + { + // Cleanup + _eventBus?.Unsubscribe(OnTestEvent); + _stateMachine?.Clear(); + _hierarchicalSM?.Clear(); + } +} + +// Test event class +public class TestEvent +{ + public string Message { get; } + public int Value { get; } + + public TestEvent(string message, int value) + { + Message = message; + Value = value; + } +} + +// Player state classes for testing +public abstract class PlayerState : State +{ + protected string StateName; + + public override void Enter() + { + GD.Print($" → Entering {StateName}"); + } + + public override void Update(float delta) { } + + public override void FixedUpdate(float fixedDelta) { } + + public override void Exit() + { + GD.Print($" → Exiting {StateName}"); + } +} + +public class IdlePlayerState : PlayerState +{ + public IdlePlayerState() + { + StateName = "Idle"; + } +} + +public class RunningPlayerState : PlayerState +{ + public RunningPlayerState() + { + StateName = "Running"; + } +} + +public class JumpingPlayerState : PlayerState +{ + public JumpingPlayerState() + { + StateName = "Jumping"; + } +} diff --git a/Scripts/Testing/LibraryTest.cs.uid b/Scripts/Testing/LibraryTest.cs.uid new file mode 100644 index 0000000..eaee72a --- /dev/null +++ b/Scripts/Testing/LibraryTest.cs.uid @@ -0,0 +1 @@ +uid://dho35wh8iaest diff --git a/Voider.csproj b/Voider.csproj index 1c0ff53..c2d7485 100644 --- a/Voider.csproj +++ b/Voider.csproj @@ -10,5 +10,6 @@ + diff --git a/project.godot b/project.godot index 736d856..d1fab43 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="Voider" -run/main_scene="res://Scenes/Main.tscn" +run/main_scene="res://Scenes/Core/Main.tscn" config/features=PackedStringArray("4.5", "C#", "Forward Plus") [dotnet]