Game state manager and restored inventory

This commit is contained in:
Marco 2025-06-25 15:36:50 +02:00
commit a0ec2f3d74
27 changed files with 1283 additions and 1175 deletions

View file

@ -1,4 +1,5 @@
using Godot;
using Cirno.Scripts.Utils;
using Godot;
using Godot.Collections;
namespace Cirno.Scripts.Activables;
@ -36,7 +37,7 @@ public partial class DialogueStarter : ChainActivable
private void OnTimelineEnded()
{
_gameManager.ChangeState(GameState.Playing);
GameStateManager.SetState(GameState.Playing);
if (_dialogic.IsConnected("timeline_ended", Callable.From(OnTimelineEnded)))
{
_dialogic.Disconnect("timeline_ended", Callable.From(OnTimelineEnded));
@ -60,7 +61,7 @@ public partial class DialogueStarter : ChainActivable
var dialogicNode = _dialogic.Call("start", _trackName);
((Node)dialogicNode).ProcessMode = ProcessModeEnum.Always;
_gameManager.ChangeState(GameState.Dialogue);
GameStateManager.SetState(GameState.Dialogue);
return true;
// Script dialogic = ResourceLoader.Load("res://addons/dialogic/Other/DialogicClass.gd") as Script;
@ -73,7 +74,7 @@ public partial class DialogueStarter : ChainActivable
{
_dialogueActive = false;
Hud.Instance?.HideHud();
_gameManager.ChangeState(GameState.Playing);
GameStateManager.SetState(GameState.Playing);
ActivateTargets();
// foreach (var activationTarget in _dialogueEndActivationTargets)
// {

View file

@ -1,6 +1,7 @@
using System.Linq;
using Cirno.Scripts.Resources;
using Cirno.Scripts.UI;
using Cirno.Scripts.Utils;
using Godot;
using Godot.Collections;
@ -18,7 +19,7 @@ public partial class VendingMachine : Interactable
public override bool Activate(ActivationType activationType = ActivationType.Toggle)
{
GameManager.Instance.ChangeState(GameState.Shop);
GameStateManager.SetState(GameState.Shop);
var ui = UiScene.Instantiate<VendingMachineUi>();

View file

@ -47,14 +47,15 @@ public partial class EnemyPossessionMovement : ActorFreeMovement
{
Shoot();
}
if (GetActionJustPressed(ControlEndAction))
{
if (GameManager.Instance.ToggleControlMode() is GameState.Playing)
{
ResumeControl();
}
}
// TODO: Restore control
// if (GetActionJustPressed(ControlEndAction))
// {
// if (GameManager.Instance.ToggleControlMode() is GameState.Playing)
// {
// ResumeControl();
// }
// }
}
private void Shoot()

View file

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.Components.Actors;
@ -25,7 +26,7 @@ public partial class KeyboardInputProvider : InputProvider
[Export] private StringName _nextWeaponActionName = "next_weapon";
[Export] private StringName _previousWeaponActionName = "previous_weapon";
[Export] private StringName _inventoryActionName = "inventory";
[Export] private StringName _pauseActionName = "pause";
[Export] private StringName _freezeActionName = "Freeze";
[Export] private StringName _reloadActionName = "Reload";
@ -54,20 +55,7 @@ public partial class KeyboardInputProvider : InputProvider
GD.Print("Mouse aim provider is null");
}
if (GameManager.Instance is null)
{
GD.Print("No GameManager found for keyboard inputprovider");
}
else
{
GameManager.Instance.GameStateChange += InstanceOnGameStateChange;
}
if (GameController.Instance is not null)
{
GameController.Instance.GameStateChange += InstanceOnGameStateChange;
}
GameStateManager.Instance.GameStateChange += InstanceOnGameStateChange;
_enabled = true;
}
@ -178,7 +166,7 @@ public partial class KeyboardInputProvider : InputProvider
public override bool GetPauseJustPressed()
{
return GetActionJustPressed(_pauseActionName);
return GlobalInputManager.IsPauseJustPressed();
}
public override bool GetFreezeJustPressed()

View file

@ -1,4 +1,6 @@
using Godot;
using Cirno.Scripts.Components.Actors;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.Components.FSM._3DPlayer;
@ -7,6 +9,7 @@ public partial class Active : BaseState<PlayerState, CharacterBody3D>
public override PlayerState StateId => PlayerState.Active;
private CharacterBody3D _player;
[Export] private InputProvider _inputProvider;
public override void Init(IStateMachine<PlayerState, CharacterBody3D> machine)
{
@ -84,12 +87,28 @@ public partial class Active : BaseState<PlayerState, CharacterBody3D>
{
base.ProcessState(delta);
HandleInputHotkeys();
}
private void HandleInputHotkeys()
{
if (_inputProvider.GetInventoryJustPressed())
{
GameStateManager.SetState(GameState.Inventory);
return;
}
if (_inputProvider.GetPauseJustPressed())
{
GameStateManager.Instance.Pause();
//PauseDeferred();
return;
}
}
private void PauseDeferred()
{
GameManager.Instance.Pause();
GameStateManager.Instance.Pause();
}

View file

@ -1,5 +1,6 @@
using System;
using Cirno.Scripts.Components.Actors;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.Components.FSM.Player;
@ -180,7 +181,7 @@ public partial class Active : PlayerStateBase
if (_inputProvider.GetInventoryJustPressed())
{
GameManager.Instance.ChangeState(GameState.Inventory);
GameStateManager.SetState(GameState.Inventory);
}
if (_inputProvider.GetPauseJustPressed())
@ -192,7 +193,7 @@ public partial class Active : PlayerStateBase
private void PauseDeferred()
{
GameManager.Instance.Pause();
GameStateManager.Instance.Pause();
}
private void HandleShoot()

View file

@ -1,4 +1,5 @@
using Cirno.Scripts.Components.Actors;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.Components.FSM.Player.Hacking;
@ -180,7 +181,7 @@ public partial class HackingActive : PlayerStateBase
if (_inputProvider.GetInventoryJustPressed())
{
GameManager.Instance.ChangeState(GameState.Inventory);
GameStateManager.SetState(GameState.Inventory);
}
if (_inputProvider.GetPauseJustPressed())
@ -192,7 +193,7 @@ public partial class HackingActive : PlayerStateBase
private void PauseDeferred()
{
GameManager.Instance.Pause();
GameStateManager.Instance.Pause();
}
private void HandleShoot()

View file

@ -3,6 +3,7 @@ using Cirno.Scripts.Components.FSM._3DPlayer;
using Cirno.Scripts.Enums;
using Cirno.Scripts.Misc;
using Cirno.Scripts.Resources;
using Cirno.Scripts.Utils;
using Godot;
using Godot.Collections;
@ -19,16 +20,10 @@ public partial class GameController : Node
[Export] private Marker3D _cameraTarget;
public Vector3? PlayerPosition => _player?.GlobalPosition ?? null;
public Vector3? PlayerVelocity => _player?.Velocity ?? null;
public GameState GameState { get; private set; }
[Signal]
public delegate void GameStateChangeEventHandler(GameState state);
[Signal]
public delegate void ManagerReadyEventHandler();
[Export] public StringName PauseActionName { get; private set; } = "pause";
[Export] public MapResource MapResource { get; private set; }
[Export] public PackedScene PlayerTemplate { get; set; }
@ -43,6 +38,8 @@ public partial class GameController : Node
private Vector3 _lastCheckPointPosition;
private GameState GameState => GameStateManager.Instance.GameState;
public Vector3 LastCheckPointPosition
{
get => _lastCheckPointPosition;
@ -52,6 +49,12 @@ public partial class GameController : Node
public override void _Ready()
{
Instance = this;
var gsm = new GameStateManager();
gsm.ProcessMode = ProcessModeEnum.Always;
this.AddChild(gsm);
gsm.Init(GameState.Playing);
RenderingServer.SetDefaultClearColor(Colors.Black);
if (GlobalState.Instance.SessionSettings.GameMode is GameMode.Roguelite)
{
@ -90,7 +93,7 @@ public partial class GameController : Node
if (_hud != null)
{
this.GameStateChange += _hud.OnGameStateChanged;
GameStateManager.Instance.GameStateChange += _hud.OnGameStateChanged;
}
if (_inventoryManager != null && _hud != null)
@ -100,7 +103,7 @@ public partial class GameController : Node
//PlayerRespawned += OnPlayerRespawned;
GameState = GameState.Playing;
//GameStateManager.Instance.GameState = GameState.Playing;
CallDeferred(MethodName.DelayPlayerSpawn);
@ -112,86 +115,6 @@ public partial class GameController : Node
{
EmitSignalManagerReady();
}
public override void _Process(double delta)
{
if (GameState is GameState.Paused && Input.IsActionJustPressed(PauseActionName))
{
Unpause();
}
}
public void Pause()
{
if (GameState == GameState.Playing)
{
ChangeState(GameState.Paused);
}
}
public void Unpause()
{
if (GameState == GameState.Paused)
{
CallDeferred(MethodName.ChangeState, (int)GameState.Playing);
//ChangeState(GameState.Playing);
}
}
public GameState ToggleControlMode()
{
if (GameState is GameState.Playing)
{
ChangeState(GameState.Controlling);
}
else if (GameState is GameState.Controlling)
{
ChangeState(GameState.Playing);
}
return GameState;
}
public void ChangeState(GameState state)
{
if (state == GameState) return;
GameState = state;
EmitSignal(SignalName.GameStateChange, (int)state);
GD.Print($"Game state changed to {state}");
switch (state)
{
case GameState.Paused:
case GameState.Dialogue:
case GameState.Shop:
case GameState.Inventory:
GlobalState.Instance.ChangeCursor(true);
GetTree().SetPause(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
break;
case GameState.Playing:
case GameState.Controlling:
//Input.MouseMode = Input.MouseModeEnum.Confined;
GlobalState.Instance.ChangeCursor(false);
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
case GameState.Menu:
GlobalState.Instance.ChangeCursor(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
}
}
private void DelayedUnpause()
{
GetTree().SetPause(false);
}
private void DelayPlayerSpawn()
{

View file

@ -19,8 +19,6 @@ public partial class GameManager : Node2D
private PlayerFSMProxy _player;
public GameState GameState { get; private set; }
public PlayerStateMachine Player => _player.PlayerFSM;
private Node2D _cameraTarget;
@ -52,9 +50,6 @@ public partial class GameManager : Node2D
private Node2D _bulletsContainer;
public Node2D BulletsContainer => _bulletsContainer;
[Signal]
public delegate void GameStateChangeEventHandler(GameState state);
[Signal]
public delegate void PlayerRespawnedEventHandler();
@ -76,6 +71,12 @@ public partial class GameManager : Node2D
public override void _Ready()
{
Instance = this;
var gsm = new GameStateManager();
gsm.ProcessMode = ProcessModeEnum.Always;
this.AddChild(gsm);
gsm.Init(GameState.Playing);
RenderingServer.SetDefaultClearColor(Colors.Black);
if (GlobalState.Instance.SessionSettings.GameMode is GameMode.Roguelite)
{
@ -116,7 +117,7 @@ public partial class GameManager : Node2D
if (_hud != null)
{
this.GameStateChange += _hud.OnGameStateChanged;
GameStateManager.Instance.GameStateChange += _hud.OnGameStateChanged;
}
if (_inventoryManager != null && _hud != null)
@ -128,8 +129,7 @@ public partial class GameManager : Node2D
}
PlayerRespawned += OnPlayerRespawned;
GameState = GameState.Playing;
//_ = DelayPlayerSpawn();
@ -166,14 +166,6 @@ public partial class GameManager : Node2D
//_inventoryManager.Load(settings.Items);
}
public override void _Process(double delta)
{
if (GameState is GameState.Paused && Input.IsActionJustPressed(PauseActionName))
{
Unpause();
}
}
private void DelayPlayerSpawn()
{
if (SpawnMarkers.Any())
@ -316,73 +308,6 @@ public partial class GameManager : Node2D
// }
// }
public void Pause()
{
if (GameState == GameState.Playing)
{
ChangeState(GameState.Paused);
}
}
public void Unpause()
{
if (GameState == GameState.Paused)
{
CallDeferred(MethodName.ChangeState, (int)GameState.Playing);
//ChangeState(GameState.Playing);
}
}
public GameState ToggleControlMode()
{
if (GameState is GameState.Playing)
{
ChangeState(GameState.Controlling);
}
else if (GameState is GameState.Controlling)
{
ChangeState(GameState.Playing);
}
return GameState;
}
public void ChangeState(GameState state)
{
if (state == GameState) return;
GameState = state;
EmitSignal(SignalName.GameStateChange, (int)state);
GD.Print($"Game state changed to {state}");
switch (state)
{
case GameState.Paused:
case GameState.Dialogue:
case GameState.Shop:
case GameState.Inventory:
GlobalState.Instance.ChangeCursor(true);
GetTree().SetPause(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
break;
case GameState.Playing:
case GameState.Controlling:
//Input.MouseMode = Input.MouseModeEnum.Confined;
GlobalState.Instance.ChangeCursor(false);
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
case GameState.Menu:
GlobalState.Instance.ChangeCursor(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
}
}
private void DelayedUnpause()
{
GetTree().SetPause(false);

View file

@ -311,39 +311,6 @@ public partial class Hud : CanvasLayer
}
}
// public void RemoveInventoryItem(string itemKey, int currentAmount)
// {
// if (_items.TryGetValue(itemKey, out var itm))
// {
// if (currentAmount <= 0)
// {
// itm.Container.QueueFree();
// _items.Remove(itemKey);
// }
// else
// {
// if (itm.Item.UiType == UiItemType.IconText)
// {
// itm.Label.Text = currentAmount.ToString();
// }
// }
// }
// else
// {
// GD.Print($"Tried to remove item {itemKey} but it was not found");
// }
//
// // var containerItem = _items.FirstOrDefault(x => x.Item == item);
// // if (containerItem == null)
// // {
// // GD.Print($"Tried to remove item {item.ItemName} but it was not found");
// // return;
// // }
// //
// // containerItem.Container.QueueFree();
//
// }
private void SpawnDebugMenu()
{
ClearPauseMenu();
@ -364,7 +331,7 @@ public partial class Hud : CanvasLayer
menu.MenuClosed += () =>
{
DebugMenuHolder.Visible = false;
if (GameManager.Instance.GameState is GameState.Paused)
if (GameStateManager.Instance.GameState is GameState.Paused)
{
SpawnPauseMenu();
}

View file

@ -9,6 +9,7 @@ using Cirno.Scripts.Resources;
using Godot.Collections;
using System.Threading.Tasks;
using Cirno.Scripts.Controllers;
using Cirno.Scripts.Utils;
public partial class PlayerMovement : CharacterBody2D, IDestructible
{
@ -157,7 +158,7 @@ public partial class PlayerMovement : CharacterBody2D, IDestructible
//_gameManager = this.GetGameManager();
_inventoryManager = this.GetInventoryManager();
_gameManager.GameStateChange += GameManagerOnGameStateChange;
GameStateManager.Instance.GameStateChange += GameManagerOnGameStateChange;
if (SelectorScene != null)
{

View file

@ -27,10 +27,11 @@ public partial class ControlActorEvent : EventResource
if (_parent.GetNode<Node2D>(Target) is Actor enemy)
{
if (GameManager.Instance.ToggleControlMode() is GameState.Controlling)
{
enemy.AssumeControl();
}
// TODO: Restore control
// if (GameManager.Instance.ToggleControlMode() is GameState.Controlling)
// {
// enemy.AssumeControl();
// }
}
_isComplete = true;

View file

@ -3,6 +3,7 @@ using System;
using Cirno.Scripts;
using Cirno.Scripts.Resources;
using Cirno.Scripts.Resources.DebugMenu;
using Cirno.Scripts.Utils;
using Godot.Collections;
using DebugMapSelectData = Cirno.Scripts.Resources.DebugMenu.DebugMapSelectData;
@ -26,9 +27,9 @@ public partial class DebugMenu : MenuBase
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
if (GameManager.Instance is not null)
if (GameStateManager.Instance is not null)
{
GameManager.Instance.GameStateChange += OnGameStateChange;
GameStateManager.Instance.GameStateChange += OnGameStateChange;
}
DefaultSelectedButton.GrabFocus();
@ -63,7 +64,7 @@ public partial class DebugMenu : MenuBase
{
if (state is not GameState.Paused)
{
GameManager.Instance.GameStateChange -= OnGameStateChange;
GameStateManager.Instance.GameStateChange -= OnGameStateChange;
CloseMenu();
}
@ -91,9 +92,9 @@ public partial class DebugMenu : MenuBase
private void ButtonOnPressed(DebugMapSelectResource scene)
{
if (GameManager.Instance is not null)
if (GameStateManager.Instance is not null)
{
GameManager.Instance.Unpause();
GameStateManager.Instance.Unpause();
}
GlobalState.Instance.GoToScene(scene.ScenePath, scene.StartData);
}

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.UI;
@ -29,7 +30,7 @@ public partial class InventoryMenu : TabContainer
{
if (Visible)
{
GameManager.Instance.ChangeState(GameState.Playing);
GameStateManager.Instance.ChangeState(GameState.Playing);
//CallDeferred(MethodName.HideInventory);
}
// else
@ -55,9 +56,9 @@ public partial class InventoryMenu : TabContainer
//ItemActivated += OnItemSelected;
// TODO: Move this on the game manager/controller side
if (GameManager.Instance is not null)
if (GameStateManager.Instance is not null)
{
GameManager.Instance.GameStateChange += state =>
GameStateManager.Instance.GameStateChange += state =>
{
switch (state)
{

View file

@ -3,6 +3,7 @@ using System;
using System.Linq;
using Cirno.Scripts;
using Cirno.Scripts.UI;
using Cirno.Scripts.Utils;
using Godot.Collections;
public partial class ItemsMenu : ItemList
@ -53,7 +54,7 @@ public partial class ItemsMenu : ItemList
InventoryManager.Instance.TryGetItem(item, out var lootItem);
if (!lootItem.Item.Selectable) return;
GameManager.Instance.ChangeState(GameState.Playing);
GameStateManager.Instance.ChangeState(GameState.Playing);
InventoryManager.Instance.UseItem(item);
}

View file

@ -1,12 +1,10 @@
using Godot;
using Cirno.Scripts.Utils;
using Godot;
namespace Cirno.Scripts.UI;
public partial class PauseMenu : Control
{
[Export]
public StringName PauseActionName = "pause";
[ExportGroup("Scenes")]
[Export]
public string MainMenuScene { get; private set; }
@ -43,9 +41,9 @@ public partial class PauseMenu : Control
public override void _Process(double delta)
{
if (_gameManager.GameState == GameState.Paused && Input.IsActionJustPressed(PauseActionName))
if (GameStateManager.Instance.GameState is GameState.Paused && GlobalInputManager.IsPauseJustPressed())
{
_gameManager.Unpause();
GameStateManager.Instance.Unpause();
}
}
@ -56,12 +54,12 @@ public partial class PauseMenu : Control
private void ResumeButtonOnPressed()
{
_gameManager.Unpause();
GameStateManager.Instance.Unpause();
}
private void QuitButtonOnPressed()
{
_gameManager.Unpause();
GameStateManager.Instance.Unpause();
GlobalState.Instance.ChangeCursor(true);
GlobalState.Instance.GotoScene(MainMenuScene);
//GetTree().ChangeSceneToFile(MainMenuScene);

View file

@ -1,6 +1,7 @@
using System.Linq;
using Cirno.Scripts.Actors;
using Cirno.Scripts.Resources;
using Cirno.Scripts.Utils;
using Godot;
using Godot.Collections;
@ -74,7 +75,7 @@ public partial class VendingMachineUi : CanvasLayer
private void Exit()
{
GD.Print("Closing");
GameManager.Instance.ChangeState(GameState.Playing);
GameStateManager.Instance.ChangeState(GameState.Playing);
QueueFree();
}
}

View file

@ -40,14 +40,7 @@ public partial class DialogueTools : RefCounted
private void ChangeState(GameState state)
{
if (GameManager.Instance is not null)
{
GameManager.Instance.ChangeState(state);
}
else if (GameController.Instance is not null)
{
GameController.Instance.ChangeState(state);
}
GameStateManager.Instance.ChangeState(state);
}
private void CreateSkipListener(Node parent)

View file

@ -0,0 +1,90 @@
using Godot;
namespace Cirno.Scripts.Utils;
public partial class GameStateManager : Node
{
public static GameStateManager Instance { get; private set; }
public GameState GameState { get; private set; }
[Signal] public delegate void GameStateChangeEventHandler(GameState state);
public void Init(GameState initialState)
{
Instance = this;
GameState = initialState;
}
public void Pause()
{
if (GameState == GameState.Playing)
{
ChangeState(GameState.Paused);
}
}
public void Unpause()
{
if (GameState == GameState.Paused)
{
CallDeferred(MethodName.ChangeState, (int)GameState.Playing);
//ChangeState(GameState.Playing);
}
}
public static void SetState(GameState state)
{
GameStateManager.Instance.ChangeState(state);
}
public void ChangeState(GameState state)
{
if (state == GameState) return;
GameState = state;
EmitSignal(SignalName.GameStateChange, (int)state);
GD.Print($"Game state changed to {state}");
switch (state)
{
case GameState.Paused:
case GameState.Dialogue:
case GameState.Shop:
case GameState.Inventory:
GlobalState.Instance.ChangeCursor(true);
GetTree().SetPause(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
break;
case GameState.Playing:
case GameState.Controlling:
//Input.MouseMode = Input.MouseModeEnum.Confined;
GlobalState.Instance.ChangeCursor(false);
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
case GameState.Menu:
GlobalState.Instance.ChangeCursor(true);
//Input.MouseMode = Input.MouseModeEnum.Visible;
DelayedUnpause();
//CallDeferred(MethodName.DelayedUnpause);
//GetTree().SetPause(false);
break;
}
}
private void DelayedUnpause()
{
GetTree().SetPause(false);
}
public override void _Process(double delta)
{
if (GameState is GameState.Paused && GlobalInputManager.IsPauseJustPressed())
{
Unpause();
return;
}
}
}

View file

@ -0,0 +1 @@
uid://dgt75vijkdgxy

View file

@ -0,0 +1,19 @@
using Godot;
namespace Cirno.Scripts.Utils;
public partial class GlobalInputManager : Node
{
public static GlobalInputManager Instance;
[Export] public StringName PauseActionName { get; private set; } = "pause";
public static bool IsPauseJustPressed()
{
return Input.IsActionJustPressed(Instance.PauseActionName);
}
public override void _Ready()
{
Instance = this;
}
}

View file

@ -0,0 +1 @@
uid://y776grpi3a30