From b6cc5a00e88086b6773b39152825b908fc4575a9 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Thu, 26 Feb 2026 23:13:57 +0100 Subject: [PATCH] Add FSM components for player and enemy state management, including initialization and module resolution --- .github/copilot-instructions.md | 0 IsoTest/IsoMapTest3.tscn | 24 ++-- Scenes/Actors/Generic_Enemy_FSM_3D.tscn | 76 ++++++------ Scripts/Activables/3D/LevelTeleporter3D.cs | 1 + Scripts/Activables/PlayerMover.cs | 1 + .../Actors/EnemyPossessionMovement.cs | 1 + Scripts/Components/FSM/3DPlayer/Active.cs | 45 +++---- Scripts/Components/FSM/3DPlayer/Cutscene.cs | 16 +-- Scripts/Components/FSM/3DPlayer/Dead.cs | 31 +++-- Scripts/Components/FSM/3DPlayer/Drowning.cs | 7 +- Scripts/Components/FSM/3DPlayer/Init.cs | 15 +-- .../FSM/3DPlayer/IsoMovementModule.cs | 8 +- .../FSM/3DPlayer/IsoPlayerStorageModule.cs | 7 +- .../FSM/3DPlayer/PlayerAcidDeathModule.cs | 7 +- .../Components/FSM/3DPlayer/PlayerState.cs | 13 ++ .../FSM/3DPlayer/PlayerState.cs.uid | 1 + .../FSM/3DPlayer/PlayerWeaponModule3D.cs | 9 +- .../Components/FSM/3DPlayer/Teleporting.cs | 22 +--- .../Components/FSM/3DPlayer/UnTeleporting.cs | 28 +---- Scripts/Components/FSM/Enemy/3D/Idle.cs | 14 ++- Scripts/Components/FSM/Enemy/3D/Init.cs | 32 ----- Scripts/Components/FSM/Enemy/3D/Init.cs.uid | 1 - Scripts/Components/FSM/Enemy/3D/InitState.cs | 72 +++++++++++ .../Components/FSM/Enemy/3D/InitState.cs.uid | 1 + Scripts/Components/FSM/Enemy/3D/Shooting.cs | 65 +++++----- Scripts/Components/FSM/Enemy/Alert.cs | 20 +++- Scripts/Components/FSM/Enemy/Controlled.cs | 22 ++-- Scripts/Components/FSM/Enemy/Dead.cs | 18 ++- .../FSM/Enemy/EnemyStorageModule.cs | 5 +- Scripts/Components/FSM/Enemy/Idle.cs | 21 ++-- Scripts/Components/FSM/Enemy/Init.cs.uid | 1 - .../FSM/Enemy/{Init.cs => InitState.cs} | 15 ++- Scripts/Components/FSM/Enemy/InitState.cs.uid | 1 + Scripts/Components/FSM/Enemy/Shooting.cs | 21 ++-- Scripts/Components/FSM/IActorStorage.cs | 11 ++ Scripts/Components/FSM/IActorStorage.cs.uid | 1 + Scripts/Components/FSM/IFSMStorage.cs | 2 +- Scripts/Components/FSM/IStateMachine.cs | 6 +- Scripts/Components/FSM/IsoStateMachineBase.cs | 30 ++++- Scripts/Components/FSM/ModuleBase.cs | 11 +- Scripts/Components/FSM/Player/Active.cs | 112 ++++++++++-------- Scripts/Components/FSM/Player/Drowning.cs | 5 +- Scripts/Components/FSM/Player/Init.cs | 29 +++-- .../FSM/Player/PlayerFSMItemUseModule.cs | 94 +-------------- Scripts/Components/FSM/Player/Teleporting.cs | 4 +- .../Components/FSM/Player/UnTeleporting.cs | 7 +- Scripts/Components/FSM/StateMachineBase.cs | 57 +++++++-- Scripts/PlayerMovement.cs | 14 +-- Scripts/Resources/Events/ControlEnemyEvent.cs | 3 +- Scripts/Resources/Events/MovePlayerEvent.cs | 1 + .../ItemEffects/SpiderbombEffectResource.cs | 3 +- addons/weapon_creator/BaseViewer.gd | 3 +- addons/weapon_creator/BulletCreatorDialog.gd | 3 +- addons/weapon_creator/BulletViewer.gd | 3 +- addons/weapon_creator/EnemyCreatorDialog.gd | 5 +- addons/weapon_creator/EnemyViewer.gd | 4 +- addons/weapon_creator/WeaponCreatorPlugin.gd | 1 - 57 files changed, 525 insertions(+), 505 deletions(-) delete mode 100644 .github/copilot-instructions.md create mode 100644 Scripts/Components/FSM/3DPlayer/PlayerState.cs create mode 100644 Scripts/Components/FSM/3DPlayer/PlayerState.cs.uid delete mode 100644 Scripts/Components/FSM/Enemy/3D/Init.cs delete mode 100644 Scripts/Components/FSM/Enemy/3D/Init.cs.uid create mode 100644 Scripts/Components/FSM/Enemy/3D/InitState.cs create mode 100644 Scripts/Components/FSM/Enemy/3D/InitState.cs.uid delete mode 100644 Scripts/Components/FSM/Enemy/Init.cs.uid rename Scripts/Components/FSM/Enemy/{Init.cs => InitState.cs} (62%) create mode 100644 Scripts/Components/FSM/Enemy/InitState.cs.uid create mode 100644 Scripts/Components/FSM/IActorStorage.cs create mode 100644 Scripts/Components/FSM/IActorStorage.cs.uid diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index e69de29b..00000000 diff --git a/IsoTest/IsoMapTest3.tscn b/IsoTest/IsoMapTest3.tscn index 79833709..152f47f1 100644 --- a/IsoTest/IsoMapTest3.tscn +++ b/IsoTest/IsoMapTest3.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://chpxebhg36bcb"] +[gd_scene format=3 uid="uid://chpxebhg36bcb"] [ext_resource type="PackedScene" uid="uid://bv7tjma3g7nkf" path="res://Scenes/Actors/3D/game_controller.tscn" id="1_d6hgs"] [ext_resource type="Resource" uid="uid://bxju78og0t8ca" path="res://Resources/Maps/3D/IsoMapTest3.tres" id="2_l3eiy"] @@ -16,28 +16,28 @@ reflected_light_source = 1 [sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_w45nv"] -[node name="Factory3" type="Node3D"] +[node name="Factory3" type="Node3D" unique_id=1937514110] -[node name="GameController" parent="." node_paths=PackedStringArray("_cameraTarget") instance=ExtResource("1_d6hgs")] +[node name="GameController" parent="." unique_id=1180859733 node_paths=PackedStringArray("_cameraTarget") instance=ExtResource("1_d6hgs")] _cameraTarget = NodePath("../CameraTarget") MapResource = ExtResource("2_l3eiy") SpawnMarkers = Dictionary[int, NodePath]({ 0: NodePath("../StartPosition") }) -[node name="DirectionalLight3D_Original" type="DirectionalLight3D" parent="."] +[node name="DirectionalLight3D_Original" type="DirectionalLight3D" parent="." unique_id=1574778523] transform = Transform3D(0.442606, -0.744379, 0.5, 0.287606, 0.645974, 0.707107, -0.849343, -0.169167, 0.5, 28.5973, 5.82742, 17.535) visible = false light_energy = 1.375 light_bake_mode = 1 shadow_enabled = true -[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=2188957] transform = Transform3D(0.401703, -0.767225, 0.5, 0.322119, 0.629476, 0.707107, -0.857248, -0.122987, 0.5, 28.5973, 5.82742, 17.535) light_energy = 1.375 light_bake_mode = 1 -[node name="DirectionalLight3D2" type="DirectionalLight3D" parent="."] +[node name="DirectionalLight3D2" type="DirectionalLight3D" parent="." unique_id=28867131] transform = Transform3D(0.0306036, -0.825802, -0.56313, -0.675985, -0.43211, 0.596931, -0.73628, 0.362399, -0.571454, 27.8154, 5.82742, 13.9456) light_energy = 0.932 light_bake_mode = 1 @@ -45,15 +45,15 @@ shadow_enabled = true shadow_blur = 0.51 sky_mode = 1 -[node name="StartPosition" type="Marker3D" parent="."] +[node name="StartPosition" type="Marker3D" parent="." unique_id=840698087] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.524396, 1.4927001, 1.6348801) script = ExtResource("8_jaqd3") -[node name="CameraTarget" type="Marker3D" parent="."] +[node name="CameraTarget" type="Marker3D" parent="." unique_id=1770556508] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.0461216, 2.33215, 2.5474238) script = ExtResource("9_py8ao") -[node name="Camera3D" type="Camera3D" parent="."] +[node name="Camera3D" type="Camera3D" parent="." unique_id=1465586253] physics_interpolation_mode = 1 transform = Transform3D(0.7071068, -0.49999997, 0.50000006, 0, 0.7071069, 0.70710677, -0.7071068, -0.49999997, 0.50000006, -3.7983856, 7.935, 5.0604153) projection = 1 @@ -63,12 +63,12 @@ MaxAimOffsetDistance = 16.0 CameraOffset = Vector3(8, 8, 8) TargetPath = NodePath("../CameraTarget") -[node name="TestLevel3" parent="." instance=ExtResource("11_l3eiy")] +[node name="TestLevel3" parent="." unique_id=615675558 instance=ExtResource("11_l3eiy")] -[node name="AudioStreamPlayer2D" parent="." instance=ExtResource("12_44k7r")] +[node name="AudioStreamPlayer2D" parent="." unique_id=254428228 instance=ExtResource("12_44k7r")] autoplay = true MusicData = ExtResource("13_isg5t") -[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1912346680] environment = SubResource("Environment_d6hgs") camera_attributes = SubResource("CameraAttributesPractical_w45nv") diff --git a/Scenes/Actors/Generic_Enemy_FSM_3D.tscn b/Scenes/Actors/Generic_Enemy_FSM_3D.tscn index f4513352..fe2f4b73 100644 --- a/Scenes/Actors/Generic_Enemy_FSM_3D.tscn +++ b/Scenes/Actors/Generic_Enemy_FSM_3D.tscn @@ -1,9 +1,9 @@ -[gd_scene load_steps=38 format=3 uid="uid://bh3vxmqflijgj"] +[gd_scene format=3 uid="uid://bh3vxmqflijgj"] [ext_resource type="Script" uid="uid://dwregubt4iila" path="res://Scripts/Components/FSM/Enemy/3D/EnemyProxy3D.cs" id="1_a3crc"] [ext_resource type="Resource" uid="uid://ccym6mcq4fbul" path="res://Resources/Enemies/Fairy_Guard_3D.tres" id="2_jgarc"] [ext_resource type="Script" uid="uid://c651imhj6rjsh" path="res://Scripts/Components/FSM/Enemy/3D/EnemyStateMachine3D.cs" id="2_xne4s"] -[ext_resource type="Script" uid="uid://cy34e3htvbvnl" path="res://Scripts/Components/FSM/Enemy/3D/Init.cs" id="4_jgarc"] +[ext_resource type="Script" uid="uid://dkmwqvhenu1xq" path="res://Scripts/Components/FSM/Enemy/3D/InitState.cs" id="4_2ut2v"] [ext_resource type="Script" uid="uid://jpdgfn701crh" path="res://Scripts/Components/FSM/Enemy/3D/Idle.cs" id="5_rg1hb"] [ext_resource type="Script" uid="uid://dvtdw2hcp4rm2" path="res://Scripts/Components/FSM/Enemy/3D/Alert.cs" id="6_jgarc"] [ext_resource type="Script" uid="uid://crahxykgis2bp" path="res://Scripts/Components/FSM/Enemy/3D/Shooting.cs" id="7_rg1hb"] @@ -88,36 +88,30 @@ animations = [{ "speed": 5.0 }] -[node name="Enemy" type="CharacterBody3D" node_paths=PackedStringArray("EnemyFSM")] +[node name="Enemy" type="CharacterBody3D" unique_id=1428692983 node_paths=PackedStringArray("EnemyFSM")] collision_layer = 64 collision_mask = 1553 script = ExtResource("1_a3crc") EnemyFSM = NodePath("StateMachine") EnemyResource = ExtResource("2_jgarc") -[node name="CollisionShape2D" type="CollisionShape3D" parent="."] +[node name="CollisionShape2D" type="CollisionShape3D" parent="." unique_id=397512658] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.047, 0) shape = SubResource("CapsuleShape3D_jgarc") -[node name="StateMachine" type="Node" parent="."] +[node name="StateMachine" type="Node" parent="." unique_id=146081447] script = ExtResource("2_xne4s") -[node name="Init" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "DetectionProvider", "HealthProvider", "_moduleNodes")] -script = ExtResource("4_jgarc") -Storage = NodePath("../../Storage") -DetectionProvider = NodePath("../../PlayerDetectionProvider") -HealthProvider = NodePath("../../DamageReceiver/HealthProvider") +[node name="Init" type="Node" parent="StateMachine" unique_id=321304267 node_paths=PackedStringArray("_moduleNodes")] +script = ExtResource("4_2ut2v") _moduleNodes = [NodePath("../../AnimationModule")] -[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "PlayerDetection", "GravityProvider", "_moduleNodes")] +[node name="Idle" type="Node" parent="StateMachine" unique_id=1798192567 node_paths=PackedStringArray("_moduleNodes")] script = ExtResource("5_rg1hb") -Storage = NodePath("../../Storage") -PlayerDetection = NodePath("../../PlayerDetectionProvider") -GravityProvider = NodePath("../../GravityProvider") DebugEnabled = true _moduleNodes = [NodePath("../../DamageModule"), NodePath("../../AnimationModule"), NodePath("../../SoundModule")] -[node name="Alert" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "PlayerDetection", "NavigationModule", "GravityProvider", "_moduleNodes")] +[node name="Alert" type="Node" parent="StateMachine" unique_id=2023331935 node_paths=PackedStringArray("Storage", "PlayerDetection", "NavigationModule", "GravityProvider", "_moduleNodes")] script = ExtResource("6_jgarc") Storage = NodePath("../../Storage") PlayerDetection = NodePath("../../PlayerDetectionProvider") @@ -126,32 +120,28 @@ GravityProvider = NodePath("../../GravityProvider") DebugEnabled = true _moduleNodes = [NodePath("../../DamageModule"), NodePath("../../AnimationModule"), NodePath("../../SoundModule")] -[node name="Shooting" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "PlayerDetection", "EquippedWeapon", "NavigationModule", "GravityProvider", "_moduleNodes")] +[node name="Shooting" type="Node" parent="StateMachine" unique_id=767265433 node_paths=PackedStringArray("EquippedWeapon", "_moduleNodes")] script = ExtResource("7_rg1hb") -Storage = NodePath("../../Storage") -PlayerDetection = NodePath("../../PlayerDetectionProvider") EquippedWeapon = NodePath("../../Weapon") -NavigationModule = NodePath("../../NavigationProvider") -GravityProvider = NodePath("../../GravityProvider") _moduleNodes = [NodePath("../../DamageModule"), NodePath("../../AnimationModule"), NodePath("../../SoundModule")] -[node name="Dead" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "_moduleNodes")] +[node name="Dead" type="Node" parent="StateMachine" unique_id=1312501461 node_paths=PackedStringArray("Storage", "_moduleNodes")] script = ExtResource("8_5j04l") Storage = NodePath("../../Storage") _moduleNodes = [NodePath("../../LootModule")] -[node name="Controlled" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "_moduleNodes")] +[node name="Controlled" type="Node" parent="StateMachine" unique_id=481921517 node_paths=PackedStringArray("Storage", "_moduleNodes")] script = ExtResource("9_dm2sd") Storage = NodePath("../../Storage") _moduleNodes = [NodePath("../../DamageModule")] -[node name="AnimationModule" type="Node" parent="." node_paths=PackedStringArray("AnimationProvider", "Storage", "HealthProvider")] +[node name="AnimationModule" type="Node" parent="." unique_id=920204467 node_paths=PackedStringArray("AnimationProvider", "Storage", "HealthProvider")] script = ExtResource("10_d6h7c") AnimationProvider = NodePath("../AnimationProvider") Storage = NodePath("../Storage") HealthProvider = NodePath("../DamageReceiver/HealthProvider") -[node name="AnimationProvider" type="Node3D" parent="." node_paths=PackedStringArray("AnimatedSprite")] +[node name="AnimationProvider" type="Node3D" parent="." unique_id=2053395074 node_paths=PackedStringArray("AnimatedSprite")] script = ExtResource("10_5gcuf") AnimatedSprite = NodePath("../AnimatedSprite3D") WalkRightAnimationName = &"right" @@ -160,7 +150,7 @@ WalkDownAnimationName = &"down" WalkUpAnimationName = &"up" BlinkMaterial = ExtResource("10_05pdu") -[node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="."] +[node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="." unique_id=1186020475] transform = Transform3D(0.707107, -0.5, 0.5, 0, 0.707107, 0.707107, -0.707107, -0.5, 0.5, 0, 0, 0) material_override = SubResource("ShaderMaterial_d6h7c") pixel_size = 0.05 @@ -172,38 +162,38 @@ frame = 2 frame_progress = 0.92087 script = ExtResource("11_jgarc") -[node name="Storage" type="Node" parent="." node_paths=PackedStringArray("Root")] +[node name="Storage" type="Node" parent="." unique_id=8651828 node_paths=PackedStringArray("Root")] script = ExtResource("11_xne4s") Root = NodePath("..") -[node name="PlayerDetectionProvider" type="Area3D" parent="."] +[node name="PlayerDetectionProvider" type="Area3D" parent="." unique_id=77201234] collision_layer = 0 collision_mask = 2 script = ExtResource("13_rg1hb") ObstaclesCollisionMask = 17 -[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetectionProvider"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetectionProvider" unique_id=1149305496] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.557434, 0) shape = SubResource("CylinderShape3D_5j04l") -[node name="NavigationProvider" type="Node" parent="." node_paths=PackedStringArray("NavigationAgent", "StorageModule")] +[node name="NavigationProvider" type="Node" parent="." unique_id=1851675964 node_paths=PackedStringArray("NavigationAgent", "StorageModule")] script = ExtResource("14_dm2sd") NavigationAgent = NodePath("../NavigationAgent3D") StorageModule = NodePath("../Storage") -[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."] +[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=1202654381] path_postprocessing = 1 debug_enabled = true -[node name="Weapon" parent="." instance=ExtResource("15_27vgy")] +[node name="Weapon" parent="." unique_id=771249458 instance=ExtResource("15_27vgy")] -[node name="DamageModule" type="Node" parent="." node_paths=PackedStringArray("DamageReceiver", "StorageModule", "AnimationProvider")] +[node name="DamageModule" type="Node" parent="." unique_id=430721165 node_paths=PackedStringArray("DamageReceiver", "StorageModule", "AnimationProvider")] script = ExtResource("16_27vgy") DamageReceiver = NodePath("../DamageReceiver") StorageModule = NodePath("../Storage") AnimationProvider = NodePath("../AnimationProvider") -[node name="DamageReceiver" type="Area3D" parent="." node_paths=PackedStringArray("HealthProvider")] +[node name="DamageReceiver" type="Area3D" parent="." unique_id=1792872756 node_paths=PackedStringArray("HealthProvider")] collision_layer = 64 collision_mask = 8 script = ExtResource("17_tabhk") @@ -211,20 +201,20 @@ HealthProvider = NodePath("HealthProvider") BulletGroup = 2 DeleteParentOnDeath = false -[node name="CollisionShape3D" type="CollisionShape3D" parent="DamageReceiver"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="DamageReceiver" unique_id=1457345703] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.00463867, 0) shape = SubResource("CylinderShape3D_uv2lf") -[node name="HealthProvider" type="Node" parent="DamageReceiver"] +[node name="HealthProvider" type="Node" parent="DamageReceiver" unique_id=610376220] script = ExtResource("18_4fnoq") ResourceName = "Health" -[node name="GravityProvider" type="Node" parent="."] +[node name="GravityProvider" type="Node" parent="." unique_id=2086594224] script = ExtResource("25_qg061") Gravity = -20.0 MaxFallSpeed = 50.0 -[node name="SoundModule" type="Node" parent="." node_paths=PackedStringArray("DamageReceiver", "StorageModule", "HitPlayer", "DeathPlayer", "AlertPlayer")] +[node name="SoundModule" type="Node" parent="." unique_id=540759764 node_paths=PackedStringArray("DamageReceiver", "StorageModule", "HitPlayer", "DeathPlayer", "AlertPlayer")] script = ExtResource("26_ojvcb") DamageReceiver = NodePath("../DamageReceiver") StorageModule = NodePath("../Storage") @@ -232,19 +222,19 @@ HitPlayer = NodePath("HitPlayer") DeathPlayer = NodePath("DeathPlayer") AlertPlayer = NodePath("AlertPlayer") -[node name="HitPlayer" type="AudioStreamPlayer3D" parent="SoundModule"] +[node name="HitPlayer" type="AudioStreamPlayer3D" parent="SoundModule" unique_id=1334761769] unit_size = 30.0 bus = &"Effects" -[node name="AlertPlayer" type="AudioStreamPlayer3D" parent="SoundModule"] +[node name="AlertPlayer" type="AudioStreamPlayer3D" parent="SoundModule" unique_id=1111891836] unit_size = 30.0 bus = &"Effects" -[node name="DeathPlayer" type="AudioStreamPlayer3D" parent="SoundModule"] +[node name="DeathPlayer" type="AudioStreamPlayer3D" parent="SoundModule" unique_id=1385917323] unit_size = 30.0 bus = &"Effects" -[node name="Hitbox" type="AnimatedSprite3D" parent="."] +[node name="Hitbox" type="AnimatedSprite3D" parent="." unique_id=123796697] transform = Transform3D(0.707107, -0.5, 0.5, 0, 0.707107, 0.707107, -0.707107, -0.5, 0.5, 0, 0, 0) material_overlay = SubResource("StandardMaterial3D_2ut2v") ignore_occlusion_culling = true @@ -254,11 +244,11 @@ texture_filter = 0 render_priority = -1 sprite_frames = SubResource("SpriteFrames_t121s") -[node name="LootModule" type="Node" parent="." node_paths=PackedStringArray("StorageModule")] +[node name="LootModule" type="Node" parent="." unique_id=729648693 node_paths=PackedStringArray("StorageModule")] script = ExtResource("28_2ut2v") StorageModule = NodePath("../Storage") -[node name="Shadow2" type="Decal" parent="."] +[node name="Shadow2" type="Decal" parent="." unique_id=2007780472] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.13562012, -0.118652344, 0.13336182) size = Vector3(0.5, 0.7373047, 0.5) texture_albedo = ExtResource("28_t121s") diff --git a/Scripts/Activables/3D/LevelTeleporter3D.cs b/Scripts/Activables/3D/LevelTeleporter3D.cs index be17c515..78df5d81 100644 --- a/Scripts/Activables/3D/LevelTeleporter3D.cs +++ b/Scripts/Activables/3D/LevelTeleporter3D.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Cirno.Scripts.Components.FSM; using Cirno.Scripts.Components.FSM._3DPlayer; using Godot; using Godot.Collections; diff --git a/Scripts/Activables/PlayerMover.cs b/Scripts/Activables/PlayerMover.cs index 31f9ff23..6b1c72af 100644 --- a/Scripts/Activables/PlayerMover.cs +++ b/Scripts/Activables/PlayerMover.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Cirno.Scripts.Components.FSM; using Godot; using GTweens.Builders; using GTweens.Easings; diff --git a/Scripts/Components/Actors/EnemyPossessionMovement.cs b/Scripts/Components/Actors/EnemyPossessionMovement.cs index 5f5bd8f9..cfff9609 100644 --- a/Scripts/Components/Actors/EnemyPossessionMovement.cs +++ b/Scripts/Components/Actors/EnemyPossessionMovement.cs @@ -1,5 +1,6 @@ using Cirno.Scripts; using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Components.FSM; using Godot; public partial class EnemyPossessionMovement : ActorFreeMovement diff --git a/Scripts/Components/FSM/3DPlayer/Active.cs b/Scripts/Components/FSM/3DPlayer/Active.cs index 850676f6..ad9da618 100644 --- a/Scripts/Components/FSM/3DPlayer/Active.cs +++ b/Scripts/Components/FSM/3DPlayer/Active.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Components.Actors; using Cirno.Scripts.Components.Actors._3D; using Cirno.Scripts.Utils; using Godot; @@ -14,39 +13,34 @@ public partial class Active : BaseState [Export] private InputProvider _inputProvider; [Export] public PlayerAnimationProvider3D AnimationProvider { get; private set; } - [Export] public IsoPlayerStorageModule Storage { get; private set; } + private IsoPlayerStorageModule Storage { get; set; } [Export] public PlayerDamageReceiver3D DamageReceiver { get; private set; } - //[Export] public PlayerHitboxSpriteProvider3D HitboxSpriteProvider { get; private set; } - public override void Init(IStateMachine machine) { base.Init(machine); - //_hud = Hud.Instance; + if (machine is IsoPlayerStateMachine sm) + Storage = sm.Storage; DamageReceiver.Death += () => { ChangeState(PlayerState.Dead); }; - DamageReceiver.HealthDecreased += (value, newValue, maxValue) => - { - AnimationProvider.Blink(); - }; + DamageReceiver.HealthDecreased += (_, _, _) => + { + AnimationProvider.Blink(); + }; - DamageReceiver.ShieldDecreased += (value, newValue, maxValue) => - { - AnimationProvider.PlayShieldAnimation(); - - }; + DamageReceiver.ShieldDecreased += (_, _, _) => + { + AnimationProvider.PlayShieldAnimation(); + }; DamageReceiver.Init(StateMachine); DamageReceiver.RefillHealth(); DamageReceiver.RefillShield(); - //_activationProvider.Init(MainObject); - - //_weaponProvider.Init(MainObject); } public override void EnterState() @@ -78,19 +72,10 @@ public partial class Active : BaseState DamageReceiver.Enabled = false; _canOpenInventory = true; - // _activationProvider.Enabled = false; + // _activation_provider.Enabled = false; // _interactionController.Enabled = false; } - public override void PhysicsProcessState(double delta) - { - // Reset at start of frame - //MainObject.Velocity = Vector2.Zero; - - // Process modules - base.PhysicsProcessState(delta); - } - public override void ProcessState(double delta) { base.ProcessState(delta); @@ -102,14 +87,13 @@ public partial class Active : BaseState } private bool _canOpenInventory = true; - private double _inventoryCooldown = 0; + private double _inventoryCooldown; private void HandleInputHotkeys(double delta) { if (_canOpenInventory && _inputProvider.GetInventoryJustPressed()) { _canOpenInventory = false; - GameStateManager.SetState(GameState.Inventory); return; } @@ -127,7 +111,6 @@ public partial class Active : BaseState if (_inputProvider.GetPauseJustPressed()) { GameStateManager.Instance.Pause(); - return; } } diff --git a/Scripts/Components/FSM/3DPlayer/Cutscene.cs b/Scripts/Components/FSM/3DPlayer/Cutscene.cs index fa5d8ab5..513cbe9c 100644 --- a/Scripts/Components/FSM/3DPlayer/Cutscene.cs +++ b/Scripts/Components/FSM/3DPlayer/Cutscene.cs @@ -1,5 +1,4 @@ using Cirno.Scripts.Components.Actors._3D; -using Cirno.Scripts.Components.FSM.Player; using Cirno.Scripts.Utils; using Godot; @@ -11,11 +10,13 @@ public partial class Cutscene : BaseState [Export] public PlayerAnimationProvider3D AnimationProvider { get; set; } - [Export] public IsoPlayerStorageModule PlayerStorage { get; private set; } + private IsoPlayerStorageModule PlayerStorage { get; set; } public override void Init(IStateMachine machine) { base.Init(machine); + if (machine is IsoPlayerStateMachine sm) + PlayerStorage = sm.Storage; } public override void EnterState() @@ -40,15 +41,4 @@ public partial class Cutscene : BaseState AnimationProvider.SetAnimation(MainObject.Velocity.ToVector2()); } - public override void PhysicsProcessState(double delta) - { - // Reset at start of frame - //MainObject.Velocity = Vector2.Zero; - - // Process modules - base.PhysicsProcessState(delta); - - - } - } \ No newline at end of file diff --git a/Scripts/Components/FSM/3DPlayer/Dead.cs b/Scripts/Components/FSM/3DPlayer/Dead.cs index 1ae49128..e8111187 100644 --- a/Scripts/Components/FSM/3DPlayer/Dead.cs +++ b/Scripts/Components/FSM/3DPlayer/Dead.cs @@ -9,19 +9,34 @@ public partial class Dead : BaseState { public override PlayerState StateId => PlayerState.Dead; - [Export] private ActorResourceProvider _motivationProvider; - [Export] private InputProvider _inputProvider; - [Export] private ActorResourceProvider _healthProvider; - [Export] private PlayerAnimationProvider3D _animationProvider; - private bool _isGameOver = false; - + private bool _isGameOver; + + public override void Init(IStateMachine machine) + { + base.Init(machine); + // try to obtain common providers via modules/storage + if (machine is IsoPlayerStateMachine sm) + { + var storage = sm.Storage; + // store references if present on storage + _motivationProvider = storage.Root.GetNodeOrNull("MotivationProvider"); + _healthProvider = storage.Root.GetNodeOrNull("HealthProvider"); + } + // fallback to searching on the actor + _motivationProvider ??= StateMachine.GetModule(); + _healthProvider ??= StateMachine.GetModule(); + + _animationProvider ??= StateMachine.GetModule(); + + } + public override void EnterState() { base.EnterState(); @@ -50,10 +65,6 @@ public partial class Dead : BaseState Hud.Instance.HideTerminated(); } - public override void PhysicsProcessState(double delta) - { - base.PhysicsProcessState(delta); - } public override void ProcessState(double delta) { diff --git a/Scripts/Components/FSM/3DPlayer/Drowning.cs b/Scripts/Components/FSM/3DPlayer/Drowning.cs index e436dbc3..d00f2311 100644 --- a/Scripts/Components/FSM/3DPlayer/Drowning.cs +++ b/Scripts/Components/FSM/3DPlayer/Drowning.cs @@ -7,9 +7,14 @@ public partial class Drowning : BaseState { public override PlayerState StateId => PlayerState.Drowning; - [Export] private PlayerAnimationProvider3D _animationProvider; + public override void Init(IStateMachine machine) + { + base.Init(machine); + _animationProvider ??= StateMachine.GetModule(); + } + public override void EnterState() { _animationProvider.PlayDrowningAnimation(); diff --git a/Scripts/Components/FSM/3DPlayer/Init.cs b/Scripts/Components/FSM/3DPlayer/Init.cs index c655da35..bd66f4f6 100644 --- a/Scripts/Components/FSM/3DPlayer/Init.cs +++ b/Scripts/Components/FSM/3DPlayer/Init.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Godot; +using Godot; namespace Cirno.Scripts.Components.FSM._3DPlayer; @@ -9,9 +8,9 @@ public partial class Init : BaseState public override void EnterState() { - // _storageModule.FacingDirection = ((PlayerStateMachine)StateMachine).StartingDirection; - // _animationProvider.PlayUnteleportAnimation(); - _ = AutoSwitchToStart(); + // Use Godot timer instead of Task.Delay to stay on main thread + var timer = GetTree().CreateTimer(0.5f); + timer.Timeout += () => StateMachine.SetState(PlayerState.Active); } public override void ExitState() @@ -28,10 +27,4 @@ public partial class Init : BaseState { } - - private async Task AutoSwitchToStart() - { - await Task.Delay(500); - StateMachine.SetState(PlayerState.Active); - } } \ No newline at end of file diff --git a/Scripts/Components/FSM/3DPlayer/IsoMovementModule.cs b/Scripts/Components/FSM/3DPlayer/IsoMovementModule.cs index e3183f2c..e01e89c1 100644 --- a/Scripts/Components/FSM/3DPlayer/IsoMovementModule.cs +++ b/Scripts/Components/FSM/3DPlayer/IsoMovementModule.cs @@ -8,7 +8,7 @@ namespace Cirno.Scripts.Components.FSM._3DPlayer; public partial class IsoMovementModule : ModuleBase { - [Export] public IsoPlayerStorageModule PlayerStorage { get; private set; } + private IsoPlayerStorageModule PlayerStorage { get; set; } [Export] private InputProvider _inputProvider; [Export] public PlayerHitboxSpriteProvider3D HitboxSpriteProvider { get; private set; } @@ -29,9 +29,6 @@ public partial class IsoMovementModule : ModuleBase _isStrafing ? StrafeSpeed : Speed; - private IStateMachine _stateMachine; - private CharacterBody3D MainObject => _stateMachine.MainObject; - public override void EnterState(PlayerState state) { _accelerationPerSecond = Speed / Acceleration; @@ -44,7 +41,8 @@ public partial class IsoMovementModule : ModuleBase machine) { - _stateMachine = machine; + base.Init(machine); + PlayerStorage ??= StateMachine.GetModule(); } public override void Process(double delta) diff --git a/Scripts/Components/FSM/3DPlayer/IsoPlayerStorageModule.cs b/Scripts/Components/FSM/3DPlayer/IsoPlayerStorageModule.cs index a31532ec..7bfab011 100644 --- a/Scripts/Components/FSM/3DPlayer/IsoPlayerStorageModule.cs +++ b/Scripts/Components/FSM/3DPlayer/IsoPlayerStorageModule.cs @@ -1,9 +1,11 @@ using Cirno.Scripts.Components.Actors; using Godot; +using Cirno.Scripts.Resources.Loot; +using System.Collections.Generic; namespace Cirno.Scripts.Components.FSM._3DPlayer; -public partial class IsoPlayerStorageModule : Node +public partial class IsoPlayerStorageModule : Node, IFSMStorage, IActorStorage { [Export] public IsoPlayerFSMProxy Root { get; private set; } @@ -14,4 +16,7 @@ public partial class IsoPlayerStorageModule : Node public Vector2 FacingDirection { get; set; } = Vector2.Down; public Vector2 AimingDirection { get; set; } = Vector2.Down; public Vector2 MovementDirection { get; set; } = Vector2.Zero; + + // Implement LootDrops for IFSMStorage (players may not drop loot but expose empty list) + public IEnumerable LootDrops => new LootDrop[0]; } \ No newline at end of file diff --git a/Scripts/Components/FSM/3DPlayer/PlayerAcidDeathModule.cs b/Scripts/Components/FSM/3DPlayer/PlayerAcidDeathModule.cs index 30357a9d..a72ff734 100644 --- a/Scripts/Components/FSM/3DPlayer/PlayerAcidDeathModule.cs +++ b/Scripts/Components/FSM/3DPlayer/PlayerAcidDeathModule.cs @@ -4,9 +4,6 @@ namespace Cirno.Scripts.Components.FSM._3DPlayer; public partial class PlayerAcidDeathModule : ModuleBase { - private IStateMachine _stateMachine; - private CharacterBody3D MainObject => _stateMachine.MainObject; - private bool _enabled = false; public override void EnterState(PlayerState state) @@ -21,7 +18,7 @@ public partial class PlayerAcidDeathModule : ModuleBase machine) { - _stateMachine = machine; + base.Init(machine); } public override void Process(double delta) @@ -38,6 +35,6 @@ public partial class PlayerAcidDeathModule : ModuleBase _stateMachine; - private CharacterBody3D MainObject => _stateMachine.MainObject; + private IsoPlayerStorageModule Storage { get; set; } public override void EnterState(PlayerState state) { @@ -25,8 +22,8 @@ public partial class PlayerWeaponModule3D : ModuleBase machine) { - _stateMachine = machine; - + base.Init(machine); + Storage ??= StateMachine.GetModule(); WeaponProvider.Init(MainObject); } diff --git a/Scripts/Components/FSM/3DPlayer/Teleporting.cs b/Scripts/Components/FSM/3DPlayer/Teleporting.cs index 5650ebb9..0a4172a2 100644 --- a/Scripts/Components/FSM/3DPlayer/Teleporting.cs +++ b/Scripts/Components/FSM/3DPlayer/Teleporting.cs @@ -1,6 +1,4 @@ using Cirno.Scripts.Components.Actors._3D; -using Cirno.Scripts.Components.FSM.Player; -using Cirno.Scripts.Utils; using Godot; namespace Cirno.Scripts.Components.FSM._3DPlayer; @@ -9,38 +7,24 @@ public partial class Teleporting : BaseState { public override PlayerState StateId => PlayerState.Teleporting; - [Export] public PlayerAnimationProvider3D AnimationProvider { get; set; } + private PlayerAnimationProvider3D AnimationProvider { get; set; } public override void Init(IStateMachine machine) { base.Init(machine); + AnimationProvider ??= StateMachine.GetModule(); } public override void EnterState() { base.EnterState(); - AnimationProvider.PlayTeleportAnimation(); + AnimationProvider?.PlayTeleportAnimation(); MainObject.Velocity = Vector3.Zero; } - public override void ExitState() - { - base.ExitState(); - - - } - public override void ProcessState(double delta) { base.ProcessState(delta); - - } - - public override void PhysicsProcessState(double delta) - { - // Process modules - base.PhysicsProcessState(delta); - } } \ No newline at end of file diff --git a/Scripts/Components/FSM/3DPlayer/UnTeleporting.cs b/Scripts/Components/FSM/3DPlayer/UnTeleporting.cs index a2ccd450..16ca6783 100644 --- a/Scripts/Components/FSM/3DPlayer/UnTeleporting.cs +++ b/Scripts/Components/FSM/3DPlayer/UnTeleporting.cs @@ -1,6 +1,4 @@ using Cirno.Scripts.Components.Actors._3D; -using Cirno.Scripts.Components.FSM.Player; -using Cirno.Scripts.Utils; using Godot; namespace Cirno.Scripts.Components.FSM._3DPlayer; @@ -9,37 +7,17 @@ public partial class UnTeleporting : BaseState { public override PlayerState StateId => PlayerState.UnTeleporting; - [Export] public PlayerAnimationProvider3D AnimationProvider { get; set; } + private PlayerAnimationProvider3D AnimationProvider { get; set; } public override void Init(IStateMachine machine) { base.Init(machine); + AnimationProvider ??= StateMachine.GetModule(); } public override void EnterState() { base.EnterState(); - AnimationProvider.PlayUnteleportAnimation(); + AnimationProvider?.PlayUnteleportAnimation(); } - - public override void ExitState() - { - base.ExitState(); - - - } - - public override void ProcessState(double delta) - { - base.ProcessState(delta); - - } - - public override void PhysicsProcessState(double delta) - { - // Process modules - base.PhysicsProcessState(delta); - - } - } \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/3D/Idle.cs b/Scripts/Components/FSM/Enemy/3D/Idle.cs index 4364a90d..9fe9d3df 100644 --- a/Scripts/Components/FSM/Enemy/3D/Idle.cs +++ b/Scripts/Components/FSM/Enemy/3D/Idle.cs @@ -7,15 +7,23 @@ public partial class Idle : EnemyStateBase3D { public override EnemyState StateId => EnemyState.Idle; - [Export] public EnemyStorage3D Storage { get; private set; } - [Export] public PlayerDetection3D PlayerDetection { get; private set; } + private EnemyStorage3D Storage { get; set; } + private PlayerDetection3D PlayerDetection { get; set; } - [Export] public GravityProvider GravityProvider { get; private set; } + private GravityProvider GravityProvider { get; set; } [Export] public bool DebugEnabled { get; set; } = false; private bool _isPlayerInRange = false; + public override void Init(IStateMachine machine) + { + base.Init(machine); + Storage ??= StateMachine.GetModule(); + PlayerDetection ??= StateMachine.GetModule(); + GravityProvider ??= StateMachine.GetModule(); + } + public override void EnterState() { base.EnterState(); diff --git a/Scripts/Components/FSM/Enemy/3D/Init.cs b/Scripts/Components/FSM/Enemy/3D/Init.cs deleted file mode 100644 index 23c027b7..00000000 --- a/Scripts/Components/FSM/Enemy/3D/Init.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Cirno.Scripts.Components.Actors; -using Cirno.Scripts.Enums; -using Godot; - -namespace Cirno.Scripts.Components.FSM.Enemy._3D; - -public partial class Init : EnemyStateBase3D -{ - public override EnemyState StateId => EnemyState.Init; - [Export] public EnemyStorage3D Storage {get; private set;} - - [Export] public PlayerDetection3D DetectionProvider { get; private set; } - - [Export] public ActorResourceProvider HealthProvider { get; private set; } - - public override void EnterState() - { - //DamageReceiver.HealthProvider.MaxResource = StorageModule.Root.EnemyResource.MaxHealth; - DetectionProvider.Initialize(MainObject); - Storage.AiState = Storage.Root.StartingAiState; - - Storage.HomePosition = MainObject.GlobalPosition; - // TODO: Hide wings - // TODO: Hide aiming reticule - - HealthProvider.MaxResource = Storage.Root.EnemyResource.MaxHealth; - - HealthProvider.FillResource(); - - StateMachine.SetState(EnemyState.Idle); - } -} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/3D/Init.cs.uid b/Scripts/Components/FSM/Enemy/3D/Init.cs.uid deleted file mode 100644 index 15ff4ff5..00000000 --- a/Scripts/Components/FSM/Enemy/3D/Init.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cy34e3htvbvnl diff --git a/Scripts/Components/FSM/Enemy/3D/InitState.cs b/Scripts/Components/FSM/Enemy/3D/InitState.cs new file mode 100644 index 00000000..e49b1038 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/3D/InitState.cs @@ -0,0 +1,72 @@ +using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy._3D; + +public partial class InitState : EnemyStateBase3D +{ + public override EnemyState StateId => EnemyState.Init; + private EnemyStorage3D Storage { get; set; } + private PlayerDetection3D DetectionProvider { get; set; } + private ActorResourceProvider HealthProvider { get; set; } + + private int _initRetryCount = 0; + private const int MaxInitRetries = 20; + private const float InitRetryDelaySeconds = 0.05f; + + public override void Init(IStateMachine machine) + { + base.Init(machine); + // Try an initial resolve; EnterState will retry if necessary + Storage ??= StateMachine.GetModule(); + DetectionProvider ??= StateMachine.GetModule(); + HealthProvider ??= StateMachine.GetModule(); + } + + + public override void EnterState() + { + // Attempt to complete initialization; if modules are missing, retry with a timer + TryCompleteInit(); + } + + private void TryCompleteInit() + { + // Attempt to resolve modules again in case they were attached after Init + Storage ??= StateMachine.GetModule(); + DetectionProvider ??= StateMachine.GetModule(); + HealthProvider ??= StateMachine.GetModule(); + + if (Storage is null || DetectionProvider is null || HealthProvider is null) + { + _initRetryCount++; + if (_initRetryCount <= MaxInitRetries) + { + // Schedule a retry shortly to wait for other nodes to finish _Ready + var timer = GetTree().CreateTimer(InitRetryDelaySeconds); + timer.Timeout += TryCompleteInit; + return; + } + + // Final failure: log detailed error and bail out to avoid throws + var actorName = MainObject?.Name ?? ""; + GD.PrintErr($"[InitState] Failed to resolve required modules on actor '{actorName}' after {_initRetryCount} attempts.\n" + + $" Storage: {(Storage is null ? "MISSING" : "OK")},\n" + + $" DetectionProvider: {(DetectionProvider is null ? "MISSING" : "OK")},\n" + + $" HealthProvider: {(HealthProvider is null ? "MISSING" : "OK")}"); + return; + } + + // All modules present, proceed with initialization + DetectionProvider.Initialize(MainObject); + Storage.AiState = Storage.Root.StartingAiState; + Storage.HomePosition = MainObject.GlobalPosition; + + HealthProvider.MaxResource = Storage.Root.EnemyResource.MaxHealth; + HealthProvider.FillResource(); + + // Transition to the Idle state now that initialization is complete + StateMachine.SetState(EnemyState.Idle); + } +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/3D/InitState.cs.uid b/Scripts/Components/FSM/Enemy/3D/InitState.cs.uid new file mode 100644 index 00000000..63031936 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/3D/InitState.cs.uid @@ -0,0 +1 @@ +uid://dkmwqvhenu1xq diff --git a/Scripts/Components/FSM/Enemy/3D/Shooting.cs b/Scripts/Components/FSM/Enemy/3D/Shooting.cs index 6109f113..72f717e2 100644 --- a/Scripts/Components/FSM/Enemy/3D/Shooting.cs +++ b/Scripts/Components/FSM/Enemy/3D/Shooting.cs @@ -9,20 +9,30 @@ public partial class Shooting : EnemyStateBase3D { public override EnemyState StateId => EnemyState.Shooting; - [Export] public EnemyStorage3D Storage { get; private set; } + private EnemyStorage3D Storage { get; set; } - [Export] public PlayerDetection3D PlayerDetection { get; private set; } + private PlayerDetection3D PlayerDetection { get; set; } [Export] public Weapon3D EquippedWeapon; - [Export] public NavigationProvider3D NavigationModule { get; private set; } + private NavigationProvider3D NavigationModule { get; set; } - [Export] public GravityProvider GravityProvider { get; private set; } + private GravityProvider GravityProvider { get; set; } - private bool _isPlayerInRange = false; - private Vector3? _currentStrafeTarget = null; - private float _strafeSpeed => Storage.EnemyData.StrafeSpeed; + private bool _isPlayerInRange; + private Vector3? _currentStrafeTarget; + private float StrafeSpeed => Storage.EnemyData.StrafeSpeed; + + private double _responseTimer; + + public override void Init(IStateMachine machine) + { + base.Init(machine); + Storage ??= StateMachine.GetModule(); + PlayerDetection ??= StateMachine.GetModule(); + NavigationModule ??= StateMachine.GetModule(); + GravityProvider ??= StateMachine.GetModule(); + } - private double _responseTimer = 0; public override void EnterState() { base.EnterState(); @@ -31,12 +41,13 @@ public partial class Shooting : EnemyStateBase3D PlayerDetection.PlayerOutOfRange += PlayerDetectionOnPlayerOutOfRange; - //DamageReceiver.ChangeState(true); - - //DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted; + if (Storage is null || PlayerDetection is null) return; - EquippedWeapon.WeaponData = Storage.Root.EnemyResource.Weapon; - EquippedWeapon.Init(); + if (EquippedWeapon != null) + { + EquippedWeapon.WeaponData = Storage.Root.EnemyResource.Weapon; + EquippedWeapon.Init(); + } _currentStrafeTarget = null; @@ -56,24 +67,22 @@ public partial class Shooting : EnemyStateBase3D PlayerDetection.PlayerOutOfRange -= PlayerDetectionOnPlayerOutOfRange; - //DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted; - _currentStrafeTarget = null; - - //DamageReceiver.ChangeState(false); - } public override void PhysicsProcessState(double delta) { base.PhysicsProcessState(delta); + if (Storage is null || PlayerDetection is null || NavigationModule is null || GravityProvider is null) + return; + if (PlayerDetection.IsPlayerInRange(Storage.Root.EnemyResource.ViewRange) && PlayerDetection.IsPlayerInSight()) { // SHOOT Shoot(); - if (_strafeSpeed > 0) + if (StrafeSpeed > 0) { // Check if a strafe position is needed if (!_currentStrafeTarget.HasValue || NavigationModule.IsNavigationFinished()) @@ -100,7 +109,7 @@ public partial class Shooting : EnemyStateBase3D if (_currentStrafeTarget.HasValue) { NavigationModule.SetTarget(_currentStrafeTarget.Value); - NavigationModule.Move(_strafeSpeed); + NavigationModule.Move(StrafeSpeed); } // Calculate gravity @@ -111,6 +120,8 @@ public partial class Shooting : EnemyStateBase3D private Vector3? CalculateStrafePosition() { + if (!PlayerDetection.LastKnownPlayerPosition.HasValue) return null; + var playerPos = PlayerDetection.LastKnownPlayerPosition.Value; var enemyPos = MainObject.GlobalPosition; @@ -149,16 +160,10 @@ public partial class Shooting : EnemyStateBase3D private Vector2 GetShootDirection() { - // if (Storage.EnemyData.PredictPlayer && PlayerDetection.LastKnowPlayerVelocity.HasValue) - // { - // var predictedDirection = MathFunctions.PredictInterceptPosition(MainObject.GlobalPosition, - // PlayerDetection.LastKnownPlayerPosition.Value, PlayerDetection.LastKnowPlayerVelocity.Value, - // EquippedWeapon.WeaponData.BulletData.BulletSpeed); - // if (predictedDirection.HasValue) return (predictedDirection.Value - MainObject.GlobalPosition).Normalized(); - // - // } - - return ( PlayerDetection.LastKnownPlayerPosition.Value - MainObject.GlobalPosition).ToVector2().Normalized(); + if (!PlayerDetection.LastKnownPlayerPosition.HasValue) + return Vector2.Zero; + + return (PlayerDetection.LastKnownPlayerPosition.Value - MainObject.GlobalPosition).ToVector2().Normalized(); } private void Shoot() diff --git a/Scripts/Components/FSM/Enemy/Alert.cs b/Scripts/Components/FSM/Enemy/Alert.cs index cccc6f86..6f21667a 100644 --- a/Scripts/Components/FSM/Enemy/Alert.cs +++ b/Scripts/Components/FSM/Enemy/Alert.cs @@ -8,16 +8,26 @@ public partial class Alert : EnemyStateBase { public override EnemyState StateId => EnemyState.Alert; - [Export] public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } - [Export] public PlayerDetectionModule PlayerDetection { get; private set; } + private PlayerDetectionModule PlayerDetection { get; set; } - [Export] public GenericDamageReceiver DamageReceiver { get; private set; } + private GenericDamageReceiver DamageReceiver { get; set; } - [Export] public NavigationMovementModule NavigationModule { get; private set; } + private NavigationMovementModule NavigationModule { get; set; } private bool _isPlayerInRange = false; + public override void Init(IStateMachine machine) + { + base.Init(machine); + // Try to resolve modules via the state machine so the same modules can be reused without per-state exports + StorageModule ??= StateMachine.GetModule(); + PlayerDetection ??= StateMachine.GetModule(); + DamageReceiver ??= StateMachine.GetModule(); + NavigationModule ??= StateMachine.GetModule(); + } + public override void EnterState() { base.EnterState(); @@ -25,8 +35,6 @@ public partial class Alert : EnemyStateBase PlayerDetection.SetRange(StorageModule.Root.EnemyResource.PlayerDetectionRange); - //_isPlayerInRange = PlayerDetection.IsPlayerInRange(StorageModule.Root.EnemyResource.ViewRange); - //GD.Print($"Player In Range: {_isPlayerInRange}"); PlayerDetection.PlayerInRange += PlayerDetectionOnPlayerInRange; diff --git a/Scripts/Components/FSM/Enemy/Controlled.cs b/Scripts/Components/FSM/Enemy/Controlled.cs index cd90dc60..04e853e2 100644 --- a/Scripts/Components/FSM/Enemy/Controlled.cs +++ b/Scripts/Components/FSM/Enemy/Controlled.cs @@ -8,17 +8,14 @@ public partial class Controlled : EnemyStateBase { public override EnemyState StateId => EnemyState.Controlled; - [Export] - public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } - [Export] - public GenericDamageReceiver DamageReceiver { get; private set; } + private GenericDamageReceiver DamageReceiver { get; set; } [Export] private InputProvider _inputProvider; - [Export] - public PlayerCrosshairProvider CrosshairProvider { get; private set; } + private PlayerCrosshairProvider CrosshairProvider { get; set; } [Export] public Weapon EquippedWeapon { get; private set; } @@ -28,6 +25,14 @@ public partial class Controlled : EnemyStateBase private bool _isStrafing = false; + public override void Init(IStateMachine machine) + { + base.Init(machine); + StorageModule ??= StateMachine.GetModule(); + DamageReceiver ??= StateMachine.GetModule(); + CrosshairProvider ??= StateMachine.GetModule(); + } + public override void EnterState() { base.EnterState(); @@ -80,10 +85,6 @@ public partial class Controlled : EnemyStateBase if (Input.IsActionJustPressed(ControlEndAction)) { - // if (GameManager.Instance.ToggleControlMode() is GameState.Playing) - // { - // ResumeControl(); - // } StateMachine.SetState(EnemyState.Idle); } @@ -126,7 +127,6 @@ public partial class Controlled : EnemyStateBase // Shoot at the player's last known position EquippedWeapon.ShootDirection = direction; - //StorageModule.FacingDirection = direction; //StorageModule.FacingDirection = direction.SnapToCardinal().Normalized(); EquippedWeapon.Shoot(BulletOwner.Player); diff --git a/Scripts/Components/FSM/Enemy/Dead.cs b/Scripts/Components/FSM/Enemy/Dead.cs index 02b91963..f2cff710 100644 --- a/Scripts/Components/FSM/Enemy/Dead.cs +++ b/Scripts/Components/FSM/Enemy/Dead.cs @@ -7,18 +7,16 @@ public partial class Dead : EnemyStateBase { public override EnemyState StateId => EnemyState.Dead; - [Export] - public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } - [Export] - public EnemyDropsProvider DropsProvider { get; private set; } + private EnemyDropsProvider DropsProvider { get; set; } - // public override void Init(IStateMachine machine) - // { - // base.Init(machine); - // - // - // } + public override void Init(IStateMachine machine) + { + base.Init(machine); + StorageModule ??= StateMachine.GetModule(); + DropsProvider ??= StateMachine.GetModule(); + } public override void EnterState() { diff --git a/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs index 47c07f0d..28b8105c 100644 --- a/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs +++ b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs @@ -3,16 +3,15 @@ using System.Linq; using Cirno.Scripts.Resources; using Cirno.Scripts.Resources.Loot; using Godot; -using Godot.Collections; namespace Cirno.Scripts.Components.FSM.Enemy; -public partial class EnemyStorageModule : Node2D, IFSMStorage +public partial class EnemyStorageModule : Node2D, IFSMStorage, IActorStorage { [Export] public EnemyFSMProxy Root { get; private set; } - public Node2D RootAsNode => Root; + public Node RootAsNode => Root; public EnemyResource EnemyData => Root.EnemyResource; diff --git a/Scripts/Components/FSM/Enemy/Idle.cs b/Scripts/Components/FSM/Enemy/Idle.cs index 6e7d3829..8d657b4f 100644 --- a/Scripts/Components/FSM/Enemy/Idle.cs +++ b/Scripts/Components/FSM/Enemy/Idle.cs @@ -11,21 +11,22 @@ public partial class Idle : EnemyStateBase // Scan for player, move to alert if found // Receive damage, move to alert if received - [Export] - public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } - [Export] - public PlayerDetectionModule PlayerDetection { get; private set; } + private PlayerDetectionModule PlayerDetection { get; set; } - [Export] - public GenericDamageReceiver DamageReceiver { get; private set; } + private GenericDamageReceiver DamageReceiver { get; set; } - // public override void Init(IStateMachine machine) - // { - // base.Init(machine); - // } private bool _isPlayerInRange = false; + public override void Init(IStateMachine machine) + { + base.Init(machine); + StorageModule ??= StateMachine.GetModule(); + PlayerDetection ??= StateMachine.GetModule(); + DamageReceiver ??= StateMachine.GetModule(); + } + public override void EnterState() { base.EnterState(); diff --git a/Scripts/Components/FSM/Enemy/Init.cs.uid b/Scripts/Components/FSM/Enemy/Init.cs.uid deleted file mode 100644 index 78a559b3..00000000 --- a/Scripts/Components/FSM/Enemy/Init.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://rrelumir3g6n diff --git a/Scripts/Components/FSM/Enemy/Init.cs b/Scripts/Components/FSM/Enemy/InitState.cs similarity index 62% rename from Scripts/Components/FSM/Enemy/Init.cs rename to Scripts/Components/FSM/Enemy/InitState.cs index 2cacd784..05190c37 100644 --- a/Scripts/Components/FSM/Enemy/Init.cs +++ b/Scripts/Components/FSM/Enemy/InitState.cs @@ -4,15 +4,20 @@ using Godot; namespace Cirno.Scripts.Components.FSM.Enemy; -public partial class Init : EnemyStateBase +public partial class InitState : EnemyStateBase { public override EnemyState StateId => EnemyState.Init; - [Export] - public GenericDamageReceiver DamageReceiver { get; private set; } + private GenericDamageReceiver DamageReceiver { get; set; } - [Export] - public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } + + public override void Init(IStateMachine machine) + { + base.Init(machine); + DamageReceiver ??= StateMachine.GetModule(); + StorageModule ??= StateMachine.GetModule(); + } public override void EnterState() { diff --git a/Scripts/Components/FSM/Enemy/InitState.cs.uid b/Scripts/Components/FSM/Enemy/InitState.cs.uid new file mode 100644 index 00000000..4e2c91b4 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/InitState.cs.uid @@ -0,0 +1 @@ +uid://geli5yyi4m8d diff --git a/Scripts/Components/FSM/Enemy/Shooting.cs b/Scripts/Components/FSM/Enemy/Shooting.cs index f17cf7ec..e8e78cfb 100644 --- a/Scripts/Components/FSM/Enemy/Shooting.cs +++ b/Scripts/Components/FSM/Enemy/Shooting.cs @@ -9,17 +9,13 @@ public partial class Shooting : EnemyStateBase { public override EnemyState StateId => EnemyState.Shooting; - [Export] - public EnemyStorageModule StorageModule { get; private set; } + private EnemyStorageModule StorageModule { get; set; } - [Export] - public PlayerDetectionModule PlayerDetection { get; private set; } + private PlayerDetectionModule PlayerDetection { get; set; } - [Export] - public GenericDamageReceiver DamageReceiver { get; private set; } + private GenericDamageReceiver DamageReceiver { get; set; } - [Export] - public NavigationMovementModule NavigationModule { get; private set; } + private NavigationMovementModule NavigationModule { get; set; } // [Export] public float MaxStrafeDistance { get; private set; } = 64f; // [Export] public float MinStrafeDistance { get; private set; } = 16f; @@ -34,6 +30,15 @@ public partial class Shooting : EnemyStateBase private double _responseTimer = 0; + public override void Init(IStateMachine machine) + { + base.Init(machine); + StorageModule ??= StateMachine.GetModule(); + PlayerDetection ??= StateMachine.GetModule(); + DamageReceiver ??= StateMachine.GetModule(); + NavigationModule ??= StateMachine.GetModule(); + } + public override void EnterState() { base.EnterState(); diff --git a/Scripts/Components/FSM/IActorStorage.cs b/Scripts/Components/FSM/IActorStorage.cs new file mode 100644 index 00000000..8b689305 --- /dev/null +++ b/Scripts/Components/FSM/IActorStorage.cs @@ -0,0 +1,11 @@ +using Godot; + +namespace Cirno.Scripts.Components.FSM; + +public interface IActorStorage +{ + public Vector2 FacingDirection { get; set; } + public Vector2 AimingDirection { get; set; } + public Vector2 MovementDirection { get; set; } +} + diff --git a/Scripts/Components/FSM/IActorStorage.cs.uid b/Scripts/Components/FSM/IActorStorage.cs.uid new file mode 100644 index 00000000..6328e91f --- /dev/null +++ b/Scripts/Components/FSM/IActorStorage.cs.uid @@ -0,0 +1 @@ +uid://rrdnrqkw8pna diff --git a/Scripts/Components/FSM/IFSMStorage.cs b/Scripts/Components/FSM/IFSMStorage.cs index ff4e4225..970abe75 100644 --- a/Scripts/Components/FSM/IFSMStorage.cs +++ b/Scripts/Components/FSM/IFSMStorage.cs @@ -7,5 +7,5 @@ namespace Cirno.Scripts.Components.FSM; public interface IFSMStorage { public IEnumerable LootDrops { get; } - public Node2D RootAsNode { get; } + public Node RootAsNode { get; } } \ No newline at end of file diff --git a/Scripts/Components/FSM/IStateMachine.cs b/Scripts/Components/FSM/IStateMachine.cs index d850364e..e2dc03df 100644 --- a/Scripts/Components/FSM/IStateMachine.cs +++ b/Scripts/Components/FSM/IStateMachine.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using Godot; namespace Cirno.Scripts.Components.FSM; @@ -15,4 +16,7 @@ public interface IStateMachine public TKey GetState(); public TType MainObject { get; } + + // Allow states to query modules by type from the owning actor + public T? GetModule() where T : Node; } \ No newline at end of file diff --git a/Scripts/Components/FSM/IsoStateMachineBase.cs b/Scripts/Components/FSM/IsoStateMachineBase.cs index b4519ffc..7bdde3a9 100644 --- a/Scripts/Components/FSM/IsoStateMachineBase.cs +++ b/Scripts/Components/FSM/IsoStateMachineBase.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using Godot; namespace Cirno.Scripts.Components.FSM; @@ -8,12 +9,14 @@ public abstract partial class IsoStateMachineBase : Node, IStateMac where TType : Node { public Dictionary> States { get; set; } = new(); - public TKey CurrentStateIndex { get; set; } + public TKey CurrentStateIndex { get; set; } = default!; public IState CurrentState => States[CurrentStateIndex]; public abstract TKey InitialState { get; protected set; } - private TType _mainObject; + private TType _mainObject = default!; public TType MainObject => _mainObject; + + private bool _hasState; public override void _Ready() { @@ -38,23 +41,38 @@ public abstract partial class IsoStateMachineBase : Node, IStateMac public void SetState(TKey stateId) { - if (CurrentStateIndex is not null) + if (_hasState) { + // Keep track of previous state and exit it + // PreviousStateIndex is not part of this base; derived classes may add it if needed CurrentState.ExitState(); } + CurrentStateIndex = stateId; CurrentState.EnterState(); + _hasState = true; } + // Implement the GetModule() helper so this base class satisfies the IStateMachine interface + public T? GetModule() where T : Node + { + if (MainObject is null) return default; + foreach (var child in MainObject.GetChildren()) + { + if (child is T t) return t; + } + return default; + } + public override void _Process(double delta) { - if (CurrentStateIndex is null) return; + if (!_hasState) return; CurrentState.ProcessState(delta); } public override void _PhysicsProcess(double delta) { - if (CurrentStateIndex is null) return; + if (!_hasState) return; CurrentState.PhysicsProcessState(delta); } } \ No newline at end of file diff --git a/Scripts/Components/FSM/ModuleBase.cs b/Scripts/Components/FSM/ModuleBase.cs index 5b32b7c6..1437fa74 100644 --- a/Scripts/Components/FSM/ModuleBase.cs +++ b/Scripts/Components/FSM/ModuleBase.cs @@ -6,9 +6,18 @@ public abstract partial class ModuleBase : Node, IModule StateMachine { get; private set; } + protected TType MainObject => StateMachine.MainObject; + + // Default Init behaviour sets the state machine reference. Derived classes can still override + public virtual void Init(IStateMachine machine) + { + StateMachine = machine; + } + public abstract void EnterState(TKey state); public abstract void ExitState(TKey state); - public abstract void Init(IStateMachine machine); public abstract void Process(double delta); public abstract void PhysicsProcess(double delta); } \ No newline at end of file diff --git a/Scripts/Components/FSM/Player/Active.cs b/Scripts/Components/FSM/Player/Active.cs index 6146e28d..e675601e 100644 --- a/Scripts/Components/FSM/Player/Active.cs +++ b/Scripts/Components/FSM/Player/Active.cs @@ -21,19 +21,20 @@ public partial class Active : PlayerStateBase [Export] public float Acceleration = 8f; [Export] public float Deceleration = 8f; - [ExportCategory("Providers")] [Export] private PlayerWeaponProvider _weaponProvider; - [Export] private PlayerAnimationProvider _animationProvider; - [Export] private PlayerCrosshairProvider _crosshairProvider; - [Export] private PlayerHitboxSpriteProvider _hitboxSpriteProvider; + // Providers previously exported on the state — resolve them from the actor via the state machine + private PlayerWeaponProvider _weaponProvider; + private PlayerAnimationProvider _animationProvider; + private PlayerCrosshairProvider _crosshairProvider; + private PlayerHitboxSpriteProvider _hitboxSprite_provider; - [Export] private InputProvider _inputProvider; + private InputProvider _inputProvider; - [Export] private PlayerDamageReceiver _damageReceiver; - [Export] private ActivationProvider _activationProvider; + private PlayerDamageReceiver _damageReceiver; + private ActivationProvider _activationProvider; - [Export] private InteractionController _interactionController; + private InteractionController _interaction_controller; - [Export] private PlayerStorageModule _storageModule; + private PlayerStorageModule _storageModule; private bool _isStrafing; @@ -49,31 +50,46 @@ public partial class Active : PlayerStateBase _hud = Hud.Instance; - _damageReceiver.Death += () => { ChangeState(PlayerState.Dead); }; + // Resolve modules from the actor attached to this state machine + _weaponProvider ??= StateMachine.GetModule(); + _animationProvider ??= StateMachine.GetModule(); + _crosshairProvider ??= StateMachine.GetModule(); + _hitboxSprite_provider ??= StateMachine.GetModule(); - _damageReceiver.HealthDecreased += (value, newValue, maxValue) => + _inputProvider ??= StateMachine.GetModule(); + + _damageReceiver ??= StateMachine.GetModule(); + _activationProvider ??= StateMachine.GetModule(); + + _interaction_controller ??= StateMachine.GetModule(); + + _storageModule ??= StateMachine.GetModule(); + + if (_damageReceiver != null) { - _animationProvider.Blink(); - //_hud.UpdateHealth(value, maxValue); - }; + _damageReceiver.Death += () => { ChangeState(PlayerState.Dead); }; - _damageReceiver.ShieldDecreased += (value, newValue, maxValue) => - { - _animationProvider.PlayShieldAnimation(); - //_hud.UpdateShield(value, maxValue); - }; + _damageReceiver.HealthDecreased += (_, _, _) => + { + _animationProvider?.Blink(); + //_hud.UpdateHealth(value, maxValue); + }; - _damageReceiver.Init(StateMachine); + _damageReceiver.ShieldDecreased += (_, _, _) => + { + _animationProvider?.PlayShieldAnimation(); + //_hud.UpdateShield(value, maxValue); + }; - _damageReceiver.RefillHealth(); - _damageReceiver.RefillShield(); + _damageReceiver.Init(StateMachine); - _activationProvider.Init(MainObject); + _damageReceiver.RefillHealth(); + _damageReceiver.RefillShield(); + } - //_weaponProvider = stateMachine.GetNode("WeaponProvider"); - //_animationProvider = stateMachine.GetNode("AnimationProvider"); + _activationProvider?.Init(MainObject); - _weaponProvider.Init(MainObject); + _weaponProvider?.Init(MainObject); } public override void EnterState() @@ -81,11 +97,11 @@ public partial class Active : PlayerStateBase base.EnterState(); // enable sprite // enable crosshair - _crosshairProvider.Show(); - _animationProvider.ShowSprite(); - _damageReceiver.Enabled = true; - _activationProvider.Enabled = true; - _interactionController.Enabled = true; + _crosshairProvider?.Show(); + _animationProvider?.ShowSprite(); + if (_damageReceiver != null) _damageReceiver.Enabled = true; + if (_activationProvider != null) _activationProvider.Enabled = true; + if (_interaction_controller != null) _interaction_controller.Enabled = true; _accelerationPerSecond = Speed / Acceleration; _decelerationPerSecond = Speed / Deceleration; @@ -94,14 +110,14 @@ public partial class Active : PlayerStateBase public override void ExitState() { base.ExitState(); - _animationProvider.SetAnimationSpeed(Vector2.Zero); - //_animationProvider.SetAnimation(Vector2.Zero); - _crosshairProvider.Hide(); - _hitboxSpriteProvider.Hide(); + _animationProvider?.SetAnimationSpeed(Vector2.Zero); + //_animation_provider.SetAnimation(Vector2.Zero); + _crosshairProvider?.Hide(); + _hitboxSprite_provider?.Hide(); - _damageReceiver.Enabled = false; - _activationProvider.Enabled = false; - _interactionController.Enabled = false; + if (_damageReceiver != null) _damageReceiver.Enabled = false; + if (_activationProvider != null) _activationProvider.Enabled = false; + if (_interaction_controller != null) _interaction_controller.Enabled = false; } private float _accelerationPerSecond; @@ -147,7 +163,7 @@ public partial class Active : PlayerStateBase _isStrafing = _inputProvider.GetStrafePressed(); // Toggle visibility of the hitbox sprite based on strafing - _hitboxSpriteProvider.SetVisibility(_isStrafing); + _hitboxSprite_provider?.SetVisibility(_isStrafing); var rightStickInput = _inputProvider.GetAimInput().Normalized(); @@ -164,13 +180,13 @@ public partial class Active : PlayerStateBase } // } - _animationProvider.SetAnimationSpeed(MainObject.Velocity); - _animationProvider.SetAnimation(FacingDirection); + _animationProvider?.SetAnimationSpeed(MainObject.Velocity); + _animationProvider?.SetAnimation(FacingDirection); HandleWeaponSwitch(); - _weaponProvider.Update(delta); + _weaponProvider?.Update(delta); - //_crosshairProvider.UpdatePosition(FacingDirection); + //_crosshair_provider.UpdatePosition(FacingDirection); HandleShoot(); @@ -200,28 +216,28 @@ public partial class Active : PlayerStateBase { if (_inputProvider.GetReloadJustPressed()) { - _weaponProvider.Reload(); + _weaponProvider?.Reload(); return; } if (!_inputProvider.GetShootPressed()) return; - _weaponProvider.Shoot(this.FacingDirection); + _weaponProvider?.Shoot(this.FacingDirection); } private void HandleInteraction() { - _activationProvider.HandleInteraction(); + _activationProvider?.HandleInteraction(); } private void HandleWeaponSwitch() { if (_inputProvider.GetWeaponNextJustPressed()) { - _weaponProvider.NextWeapon(); + _weaponProvider?.NextWeapon(); } else if (_inputProvider.GetWeaponPreviousJustPressed()) { - _weaponProvider.PreviousWeapon(); + _weaponProvider?.PreviousWeapon(); } } } \ No newline at end of file diff --git a/Scripts/Components/FSM/Player/Drowning.cs b/Scripts/Components/FSM/Player/Drowning.cs index 75559c76..7d104b70 100644 --- a/Scripts/Components/FSM/Player/Drowning.cs +++ b/Scripts/Components/FSM/Player/Drowning.cs @@ -6,11 +6,13 @@ public partial class Drowning : PlayerStateBase { public override PlayerState StateId => PlayerState.Drowning; - [Export] private PlayerAnimationProvider _animationProvider; public override void EnterState() { + _animationProvider ??= StateMachine.GetModule(); + if (_animationProvider is null) return; + _animationProvider.PlayDrowningAnimation(); // Wait for animation to be over and switch to death @@ -19,6 +21,7 @@ public partial class Drowning : PlayerStateBase private void AnimationProviderOnOnAnimationEnded(StringName animationName) { + if (_animationProvider is null) return; if (animationName != _animationProvider.DrowningAnimationName) return; _animationProvider.OnAnimationEnded -= AnimationProviderOnOnAnimationEnded; diff --git a/Scripts/Components/FSM/Player/Init.cs b/Scripts/Components/FSM/Player/Init.cs index 0b9f8db6..49eb69be 100644 --- a/Scripts/Components/FSM/Player/Init.cs +++ b/Scripts/Components/FSM/Player/Init.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Godot; namespace Cirno.Scripts.Components.FSM.Player; @@ -8,16 +6,27 @@ public partial class Init : PlayerStateBase { public override PlayerState StateId => PlayerState.Init; - [Export] private PlayerAnimationProvider _animationProvider; - [Export] private PlayerStorageModule _storageModule; + private PlayerStorageModule _storageModule; public override void EnterState() { - _storageModule.FacingDirection = ((PlayerStateMachine)StateMachine).StartingDirection; - _animationProvider.PlayUnteleportAnimation(); - _ = AutoSwitchToStart(); + // Resolve modules lazily to avoid method/class name conflicts with Init + _animationProvider ??= StateMachine.GetModule(); + _storageModule ??= StateMachine.GetModule(); + + if (_storageModule != null) + _storageModule.FacingDirection = ((PlayerStateMachine)StateMachine).StartingDirection; + + if (_animationProvider != null) + { + _animationProvider.PlayUnteleportAnimation(); + // If you need to wait for animation end, subscribe here + } + + var timer = GetTree().CreateTimer(0.5f); + timer.Timeout += () => StateMachine.SetState(PlayerState.Active); } public override void ExitState() @@ -34,10 +43,4 @@ public partial class Init : PlayerStateBase { } - - private async Task AutoSwitchToStart() - { - await Task.Delay(500); - StateMachine.SetState(PlayerState.Active); - } } diff --git a/Scripts/Components/FSM/Player/PlayerFSMItemUseModule.cs b/Scripts/Components/FSM/Player/PlayerFSMItemUseModule.cs index 62eec7c2..4a1870ab 100644 --- a/Scripts/Components/FSM/Player/PlayerFSMItemUseModule.cs +++ b/Scripts/Components/FSM/Player/PlayerFSMItemUseModule.cs @@ -1,5 +1,4 @@ -using System; -using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Components.Actors; using Cirno.Scripts.Resources; using Godot; @@ -13,7 +12,7 @@ public partial class PlayerFSMItemUseModule : ModuleBase _storageModule.FacingDirection = value; } - public bool Enabled { get; set; } = false; + public bool Enabled { get; set; } public override void EnterState(PlayerState state) { @@ -40,6 +39,7 @@ public partial class PlayerFSMItemUseModule : ModuleBase(); } private void UseItem(LootItem item, int totalcount) @@ -48,94 +48,8 @@ public partial class PlayerFSMItemUseModule : ModuleBase(); - // //bullet.Initialize(item.WeaponData.BulletData.MakeBullet(this.GlobalPosition, 1,0, 0), GameManager.Instance); - // - // - // //InventoryManager.Instance.RemoveItem(item.ItemKey, 1); - // // emit projectile - // var bullet = this.CreateChildOf(GameManager.Instance.BulletsContainer, item.WeaponData.BulletData.BulletScene, this.GlobalPosition); - // - // var bulletData = item.WeaponData.MakeBullet(this.GlobalPosition); - // - // bullet.Initialize(bulletData, GameManager.Instance); - // bullet.SetDirection(FacingDirection); - // bullet.RotateSpriteDegrees(-90); - // //bullet.SetDirection(_facingDirection); - // bullet.Speed = item.WeaponData.BulletData.BulletSpeed; - // - // _machine.SetState(PlayerState.Controlling); - // - // //RequestMovementDisable(true); - // // set camera - // GameManager.Instance.CameraTargetObject(bullet); - // // set event destroy - // bullet.OnDestroy += () => - // { - // GameManager.Instance.CameraTargetPlayer(); - // _machine.SetState(PlayerState.Active); - // //RequestMovementDisable(false); - // }; - // } - public override void Process(double delta) { diff --git a/Scripts/Components/FSM/Player/Teleporting.cs b/Scripts/Components/FSM/Player/Teleporting.cs index 5e19c7a2..2b4099ab 100644 --- a/Scripts/Components/FSM/Player/Teleporting.cs +++ b/Scripts/Components/FSM/Player/Teleporting.cs @@ -6,12 +6,12 @@ public partial class Teleporting : PlayerStateBase { public override PlayerState StateId => PlayerState.Teleporting; - [Export] private PlayerAnimationProvider _animationProvider; public override void EnterState() { - _animationProvider.PlayTeleportAnimation(); + _animationProvider ??= StateMachine.GetModule(); + _animationProvider?.PlayTeleportAnimation(); } public override void ExitState() diff --git a/Scripts/Components/FSM/Player/UnTeleporting.cs b/Scripts/Components/FSM/Player/UnTeleporting.cs index 1c414be9..af6b74c7 100644 --- a/Scripts/Components/FSM/Player/UnTeleporting.cs +++ b/Scripts/Components/FSM/Player/UnTeleporting.cs @@ -4,14 +4,13 @@ namespace Cirno.Scripts.Components.FSM.Player; public partial class UnTeleporting : PlayerStateBase { - public override PlayerState StateId => PlayerState.UnTeleporting - ; - [Export] + public override PlayerState StateId => PlayerState.UnTeleporting; private PlayerAnimationProvider _animationProvider; public override void EnterState() { - _animationProvider.PlayUnteleportAnimation(); + _animationProvider ??= StateMachine.GetModule(); + _animationProvider?.PlayUnteleportAnimation(); } public override void ExitState() diff --git a/Scripts/Components/FSM/StateMachineBase.cs b/Scripts/Components/FSM/StateMachineBase.cs index 0601fead..eaf89b4c 100644 --- a/Scripts/Components/FSM/StateMachineBase.cs +++ b/Scripts/Components/FSM/StateMachineBase.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using Godot; namespace Cirno.Scripts.Components.FSM; @@ -7,14 +8,21 @@ public abstract partial class StateMachineBase : Node, IStateMachin where TKey : notnull where TType : Node { + [Signal] + public delegate void StateChangedEventHandler(Variant from, Variant to); + public Dictionary> States { get; set; } = new(); public TKey CurrentStateIndex { get; set; } + public TKey PreviousStateIndex { get; private set; } public IState CurrentState => States[CurrentStateIndex]; public abstract TKey InitialState { get; protected set; } - private TType _mainObject; + private TType _mainObject = default!; public TType MainObject => _mainObject; - + + // Internal flag to indicate if a state has been set previously + private bool _hasState; + public override void _Ready() { _mainObject = this.GetParent(); @@ -30,31 +38,62 @@ public abstract partial class StateMachineBase : Node, IStateMachin } SetState(InitialState); } - + public TKey GetState() { return CurrentState.StateId; } - + public void SetState(TKey stateId) { - if (CurrentStateIndex is not null) + if (_hasState) { + PreviousStateIndex = CurrentStateIndex; CurrentState.ExitState(); } + + var from = _hasState ? CurrentStateIndex.ToString() : string.Empty; CurrentStateIndex = stateId; + var to = CurrentStateIndex.ToString(); CurrentState.EnterState(); + _hasState = true; + EmitSignal(nameof(StateChanged), from, to); } - + + /// + /// Returns the first descendant under the main object (root node that owns this state machine) + /// that matches the requested type T, or null if none found. This lets states fetch + /// modules attached to the actor without per-state editor wiring. + /// + public T? GetModule() where T : Node + { + if (MainObject is null) return default; + return FindInChildren(MainObject); + } + + private static T? FindInChildren(Node parent) where T : Node + { + foreach (var obj in parent.GetChildren()) + { + if (obj is T t) return t; + if (obj is Node node) + { + var found = FindInChildren(node); + if (found is not null) return found; + } + } + return default; + } + public override void _Process(double delta) { - if (CurrentStateIndex is null) return; + if (!_hasState) return; CurrentState.ProcessState(delta); } public override void _PhysicsProcess(double delta) { - if (CurrentStateIndex is null) return; + if (!_hasState) return; CurrentState.PhysicsProcessState(delta); } } \ No newline at end of file diff --git a/Scripts/PlayerMovement.cs b/Scripts/PlayerMovement.cs index 51f43b66..caeac8a8 100644 --- a/Scripts/PlayerMovement.cs +++ b/Scripts/PlayerMovement.cs @@ -8,6 +8,8 @@ using Cirno.Scripts.Components.Actors; using Cirno.Scripts.Resources; using Godot.Collections; using System.Threading.Tasks; +using Cirno.Scripts.Components.FSM; +using Cirno.Scripts.Components.FSM._3DPlayer; using Cirno.Scripts.Controllers; using Cirno.Scripts.Utils; @@ -586,14 +588,4 @@ public partial class PlayerMovement : CharacterBody2D, IDestructible } -public enum PlayerState -{ - Init, - Active, - Cutscene, - Teleporting, - UnTeleporting, - Controlling, - Dead, - Drowning, -} \ No newline at end of file + diff --git a/Scripts/Resources/Events/ControlEnemyEvent.cs b/Scripts/Resources/Events/ControlEnemyEvent.cs index d7955024..27a397d8 100644 --- a/Scripts/Resources/Events/ControlEnemyEvent.cs +++ b/Scripts/Resources/Events/ControlEnemyEvent.cs @@ -1,4 +1,5 @@ -using Godot; +using Cirno.Scripts.Components.FSM; +using Godot; namespace Cirno.Scripts.Resources.Events; diff --git a/Scripts/Resources/Events/MovePlayerEvent.cs b/Scripts/Resources/Events/MovePlayerEvent.cs index 1dcb0646..ba5e87b6 100644 --- a/Scripts/Resources/Events/MovePlayerEvent.cs +++ b/Scripts/Resources/Events/MovePlayerEvent.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Cirno.Scripts.Components.FSM; using Godot; using GTweens.Builders; using GTweens.Easings; diff --git a/Scripts/Resources/ItemEffects/SpiderbombEffectResource.cs b/Scripts/Resources/ItemEffects/SpiderbombEffectResource.cs index 38e9f17e..bc5e33b1 100644 --- a/Scripts/Resources/ItemEffects/SpiderbombEffectResource.cs +++ b/Scripts/Resources/ItemEffects/SpiderbombEffectResource.cs @@ -1,4 +1,5 @@ -using Cirno.Scripts.Components.FSM.Player; +using Cirno.Scripts.Components.FSM; +using Cirno.Scripts.Components.FSM.Player; using Cirno.Scripts.Controllers; using Godot; diff --git a/addons/weapon_creator/BaseViewer.gd b/addons/weapon_creator/BaseViewer.gd index 0c988de6..70e668cd 100644 --- a/addons/weapon_creator/BaseViewer.gd +++ b/addons/weapon_creator/BaseViewer.gd @@ -1,4 +1,4 @@ -@tool +@tool class_name BaseViewer extends PanelContainer @@ -173,4 +173,3 @@ func _should_show_3d() -> bool: func _log_to_dock(message: String, color: Color = Color.WHITE) -> void: if _dock and _dock.has_method("add_log"): _dock.call("add_log", message, color) - diff --git a/addons/weapon_creator/BulletCreatorDialog.gd b/addons/weapon_creator/BulletCreatorDialog.gd index c76cccce..d75ec3b4 100644 --- a/addons/weapon_creator/BulletCreatorDialog.gd +++ b/addons/weapon_creator/BulletCreatorDialog.gd @@ -1,4 +1,4 @@ -@tool +@tool extends BaseCreatorDialog # Popup window for configuring bullet parameters before creation @@ -371,4 +371,3 @@ func _show_error(message: String) -> void: dialog.title = "Error" add_child(dialog) dialog.popup_centered() - diff --git a/addons/weapon_creator/BulletViewer.gd b/addons/weapon_creator/BulletViewer.gd index 38829d4e..56564802 100644 --- a/addons/weapon_creator/BulletViewer.gd +++ b/addons/weapon_creator/BulletViewer.gd @@ -1,4 +1,4 @@ -@tool +@tool extends BaseViewer # Displays all bullets from the Resources/Bullets folders in a grid format @@ -330,4 +330,3 @@ func _open_bullet_dialog(is_3d: bool, prefill_data: Dictionary = {}) -> void: dialog.call_deferred("setup_bullet", _editor_interface, is_3d, prefill_data) dialog.call_deferred("connect", "bullet_data_confirmed", _on_duplicate_bullet_confirmed) dialog.call_deferred("popup_centered") - diff --git a/addons/weapon_creator/EnemyCreatorDialog.gd b/addons/weapon_creator/EnemyCreatorDialog.gd index 9a9a4802..15f82f43 100644 --- a/addons/weapon_creator/EnemyCreatorDialog.gd +++ b/addons/weapon_creator/EnemyCreatorDialog.gd @@ -1,4 +1,4 @@ -@tool +@tool extends BaseCreatorDialog signal enemy_data_confirmed(enemy_data: Dictionary) @@ -303,6 +303,3 @@ func _on_create_pressed() -> void: _save_dialog_size_and_position() enemy_data_confirmed.emit(enemy_data) queue_free() - - - diff --git a/addons/weapon_creator/EnemyViewer.gd b/addons/weapon_creator/EnemyViewer.gd index bf43e2eb..498dfe63 100644 --- a/addons/weapon_creator/EnemyViewer.gd +++ b/addons/weapon_creator/EnemyViewer.gd @@ -1,4 +1,4 @@ -@tool +@tool extends BaseViewer # Displays all enemies from directory structure or database @@ -224,5 +224,3 @@ func _on_create_enemy_pressed() -> void: dialog.enemy_data_confirmed.connect(func(enemy_data): duplicate_enemy_requested.emit(enemy_data) ) - - diff --git a/addons/weapon_creator/WeaponCreatorPlugin.gd b/addons/weapon_creator/WeaponCreatorPlugin.gd index 8ce90dda..7bfb97f5 100644 --- a/addons/weapon_creator/WeaponCreatorPlugin.gd +++ b/addons/weapon_creator/WeaponCreatorPlugin.gd @@ -511,4 +511,3 @@ func _create_enemy_resource(path: String, enemy_data: Dictionary) -> bool: return false return true -