using Cirno.Scripts.Components.Actors; using Cirno.Scripts.Components.Actors._3D; using Cirno.Scripts.Controllers; using Godot; namespace Cirno.Scripts.Components.FSM._3DPlayer; public partial class Dead : BaseState { public override PlayerState StateId => PlayerState.Dead; private ActorResourceProvider _motivationProvider; private InputProvider _inputProvider; private ActorResourceProvider _healthProvider; private PlayerAnimationProvider3D _animationProvider; 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(); // The input provider was not initialized after the FSM refactor which caused // a NullReferenceException in ProcessState when accessing it. Try to obtain // it from the state machine modules as well. If it's still null, ProcessState // will early-return to avoid throwing. _inputProvider ??= StateMachine.GetModule(); } public override void EnterState() { base.EnterState(); //MainObject.Hide(); // Play death animation if provider is available _animationProvider?.PlayDeathAnimation(); // If we don't have a motivation provider, assume game over and log the problem. if (_motivationProvider is null) { GD.PrintErr("[Dead] MotivationProvider not found - defaulting to Game Over"); Hud.Instance.ShowGameOver(); _isGameOver = true; return; } if (_motivationProvider.CurrentResource < 100f) { // If motivation is not enough show game over scene Hud.Instance.ShowGameOver(); _isGameOver = true; } else { // Else show respawn notification Hud.Instance.ShowTerminated(); _isGameOver = false; } } public override void ExitState() { base.ExitState(); Hud.Instance.HideTerminated(); } public override void ProcessState(double delta) { base.ProcessState(delta); // If input provider is missing, bail out to avoid NullReferenceException. if (_inputProvider is null) { // Log once to help debugging, but avoid spamming the console every frame. GD.PrintErr("[Dead] InputProvider not available - input ignored in Dead state."); return; } if (_inputProvider.GetShootJustPressed()) { if (_isGameOver) { // Restart Level GlobalState.Instance.RestartLevel(); } else { Respawn(); } } } public void Respawn() { MainObject.GlobalPosition = GameController.Instance.LastCheckPointPosition; if (_healthProvider is null) { GD.PrintErr("[Dead] HealthProvider not found - cannot refill health on respawn"); } else { _healthProvider.FillResource(); } PoolingManager.Instance.ClearBullets(); if (_motivationProvider is null) { GD.PrintErr("[Dead] MotivationProvider not found - skipping motivation deduction"); } else { _motivationProvider.CurrentResource -= 100f; if (_motivationProvider.CurrentResource <= 1f) { _motivationProvider.CurrentResource = 1f; } } ChangeState(PlayerState.Active); } }