Start refactoring UI concerns away from game and into UI logic class

This commit is contained in:
2025-03-07 16:48:45 -08:00
parent c3bfab5f53
commit 93c04440d4
11 changed files with 111 additions and 23 deletions

View File

@@ -42,8 +42,6 @@ public partial class GameLogic
public readonly record struct SaveGame; public readonly record struct SaveGame;
public readonly record struct AnnounceMessage(string Message);
public readonly record struct DoubleExpTimeStart(int lengthOfTimeInSeconds); public readonly record struct DoubleExpTimeStart(int lengthOfTimeInSeconds);
} }
} }

View File

@@ -16,7 +16,6 @@ public partial class GameLogic
gameRepo.IsPaused.Sync += OnIsPaused; gameRepo.IsPaused.Sync += OnIsPaused;
gameRepo.OpenInventory += OnOpenInventory; gameRepo.OpenInventory += OnOpenInventory;
gameRepo.CloseInventory += OnCloseInventory; gameRepo.CloseInventory += OnCloseInventory;
gameRepo.AnnounceMessage += OnAnnounceMessage;
gameRepo.DoubleExpTimeStart += OnDoubleExpTimeStart; gameRepo.DoubleExpTimeStart += OnDoubleExpTimeStart;
}); });
OnDetach(() => OnDetach(() =>
@@ -25,7 +24,6 @@ public partial class GameLogic
gameRepo.IsPaused.Sync -= OnIsPaused; gameRepo.IsPaused.Sync -= OnIsPaused;
gameRepo.OpenInventory -= OnOpenInventory; gameRepo.OpenInventory -= OnOpenInventory;
gameRepo.CloseInventory -= OnCloseInventory; gameRepo.CloseInventory -= OnCloseInventory;
gameRepo.AnnounceMessage -= OnAnnounceMessage;
gameRepo.DoubleExpTimeStart -= OnDoubleExpTimeStart; gameRepo.DoubleExpTimeStart -= OnDoubleExpTimeStart;
}); });
} }
@@ -34,8 +32,6 @@ public partial class GameLogic
private void OnCloseInventory() => Output(new Output.CloseInventory()); private void OnCloseInventory() => Output(new Output.CloseInventory());
private void OnAnnounceMessage(string message) => Output(new Output.AnnounceMessage(message));
private void OnDoubleExpTimeStart(int lengthOfTimeInSeconds) => Output(new Output.DoubleExpTimeStart(lengthOfTimeInSeconds)); private void OnDoubleExpTimeStart(int lengthOfTimeInSeconds) => Output(new Output.DoubleExpTimeStart(lengthOfTimeInSeconds));
public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused)); public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused));

View File

@@ -27,6 +27,8 @@ public interface IGameRepo : IDisposable
public void EndDoubleExp(); public void EndDoubleExp();
public void AnnounceMessageOnMainScreen(string message);
public double ExpRate { get; } public double ExpRate { get; }
} }
@@ -67,17 +69,22 @@ public class GameRepo : IGameRepo
public void StartDoubleEXP(TimeSpan lengthOfEffect) public void StartDoubleEXP(TimeSpan lengthOfEffect)
{ {
CloseInventory?.Invoke(); CloseInventory?.Invoke();
AnnounceMessage?.Invoke("Experience points temporarily doubled."); AnnounceMessageOnMainScreen("Experience points temporarily doubled.");
DoubleExpTimeStart?.Invoke(lengthOfEffect.Seconds); DoubleExpTimeStart?.Invoke(lengthOfEffect.Seconds);
ExpRate = 2; ExpRate = 2;
} }
public void EndDoubleExp() public void EndDoubleExp()
{ {
AnnounceMessage?.Invoke("Experience points effect wore off."); AnnounceMessageOnMainScreen("Experience points effect wore off.");
ExpRate = 1; ExpRate = 1;
} }
public void AnnounceMessageOnMainScreen(string message)
{
AnnounceMessage?.Invoke(message);
}
public void OnGameEnded() public void OnGameEnded()
{ {
Pause(); Pause();

View File

@@ -0,0 +1,30 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma.Implementation;
public partial class InGameUILogic
{
[Meta]
public abstract partial record State : StateLogic<State>
{
protected State()
{
OnAttach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.AnnounceMessage += OnAnnounceMessage;
});
OnDetach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.AnnounceMessage -= OnAnnounceMessage;
});
}
private void OnAnnounceMessage(string message)
{
Output(new Output.AnnounceMessage(message));
}
}
}

View File

@@ -0,0 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma.Implementation;
public interface IInGameUILogic : ILogicBlock<InGameUILogic.State>;
[Meta]
[LogicBlock(typeof(State), Diagram = true)]
public partial class InGameUILogic : LogicBlock<InGameUILogic.State>, IInGameUILogic
{
public override Transition GetInitialState() => To<State.Active>();
}

View File

@@ -0,0 +1,8 @@
namespace Zennysoft.Game.Ma.Implementation;
public partial class InGameUILogic
{
public static class Output
{
public readonly record struct AnnounceMessage(string Message);
}
}

View File

@@ -0,0 +1,15 @@
using Chickensoft.Introspection;
namespace Zennysoft.Game.Ma.Implementation;
public partial class InGameUILogic
{
public partial record State
{
[Meta]
public partial record Active : State
{
}
}
}

View File

@@ -97,6 +97,7 @@ public partial class Game : Node3D, IGame
GameLogic.Set(GameEventDepot); GameLogic.Set(GameEventDepot);
GameLogic.Set(Player); GameLogic.Set(Player);
GameLogic.Set(Map); GameLogic.Set(Map);
GameLogic.Set(InGameUI);
Instantiator = new Instantiator(GetTree()); Instantiator = new Instantiator(GetTree());
RescuedItems = new RescuedItemDatabase(); RescuedItems = new RescuedItemDatabase();
@@ -185,10 +186,10 @@ public partial class Game : Node3D, IGame
Input.MouseMode = Input.MouseModeEnum.Visible; Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(true); PauseMenu.SetProcessUnhandledInput(true);
}) })
.Handle((in GameLogic.Output.LoadNextFloor _) => { Map.SpawnNextFloor(); })
.Handle((in GameLogic.Output.LoadMap _) => { Map.LoadMap(); })
.Handle((in GameLogic.Output.HidePauseMenu _) => { PauseMenu.Hide(); }) .Handle((in GameLogic.Output.HidePauseMenu _) => { PauseMenu.Hide(); })
.Handle((in GameLogic.Output.ExitPauseMenu _) => { PauseMenu.FadeOut(); Input.MouseMode = Input.MouseModeEnum.Visible; PauseMenu.SetProcessUnhandledInput(false); }) .Handle((in GameLogic.Output.ExitPauseMenu _) => { PauseMenu.FadeOut(); Input.MouseMode = Input.MouseModeEnum.Visible; PauseMenu.SetProcessUnhandledInput(false); })
.Handle((in GameLogic.Output.LoadNextFloor _) => { Map.SpawnNextFloor(); })
.Handle((in GameLogic.Output.LoadMap _) => { Map.LoadMap(); })
.Handle((in GameLogic.Output.ShowFloorClearMenu _) => { FloorClearMenu.Show(); FloorClearMenu.FadeIn(); }) .Handle((in GameLogic.Output.ShowFloorClearMenu _) => { FloorClearMenu.Show(); FloorClearMenu.FadeIn(); })
.Handle((in GameLogic.Output.ExitFloorClearMenu _) => { FloorClearMenu.FadeOut(); }) .Handle((in GameLogic.Output.ExitFloorClearMenu _) => { FloorClearMenu.FadeOut(); })
.Handle((in GameLogic.Output.OpenInventory _) => { InGameUI.ShowInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(true); }) .Handle((in GameLogic.Output.OpenInventory _) => { InGameUI.ShowInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(true); })
@@ -199,7 +200,6 @@ public partial class Game : Node3D, IGame
.Handle((in GameLogic.Output.HideAskForTeleport _) => { GameRepo.Resume(); InGameUI.UseTeleportPrompt.FadeOut(); InGameUI.SetProcessInput(false); }) .Handle((in GameLogic.Output.HideAskForTeleport _) => { GameRepo.Resume(); InGameUI.UseTeleportPrompt.FadeOut(); InGameUI.SetProcessInput(false); })
.Handle((in GameLogic.Output.ShowLostScreen _) => { DeathMenu.Show(); DeathMenu.FadeIn(); }) .Handle((in GameLogic.Output.ShowLostScreen _) => { DeathMenu.Show(); DeathMenu.FadeIn(); })
.Handle((in GameLogic.Output.ExitLostScreen _) => { DeathMenu.FadeOut(); }) .Handle((in GameLogic.Output.ExitLostScreen _) => { DeathMenu.FadeOut(); })
.Handle((in GameLogic.Output.AnnounceMessage output) => { AnnounceMessageOnMainScreen(output.Message); })
.Handle((in GameLogic.Output.DoubleExpTimeStart output) => { DoubleEXPTimer.WaitTime = output.lengthOfTimeInSeconds; DoubleEXPTimer.Start(); }) .Handle((in GameLogic.Output.DoubleExpTimeStart output) => { DoubleEXPTimer.WaitTime = output.lengthOfTimeInSeconds; DoubleEXPTimer.Start(); })
.Handle((in GameLogic.Output.SaveGame _) => .Handle((in GameLogic.Output.SaveGame _) =>
{ {
@@ -364,11 +364,6 @@ public partial class Game : Node3D, IGame
InGameUI.InventoryMenu.ShowMessage(message); InGameUI.InventoryMenu.ShowMessage(message);
} }
public void AnnounceMessageOnMainScreen(string message)
{
InGameUI.PlayerInfoUI.DisplayMessage(message);
}
public IDungeonFloor CurrentFloor => Map.CurrentFloor; public IDungeonFloor CurrentFloor => Map.CurrentFloor;
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource) public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource)

View File

@@ -5,7 +5,6 @@ using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot; using Godot;
using System;
using Zennysoft.Game.Ma.Implementation; using Zennysoft.Game.Ma.Implementation;
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, IProvide<ISaveChunk<GameData>>, INode3D public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, IProvide<ISaveChunk<GameData>>, INode3D
@@ -32,8 +31,6 @@ public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvid
public void AnnounceMessageOnInventoryScreen(string message); public void AnnounceMessageOnInventoryScreen(string message);
public void AnnounceMessageOnMainScreen(string message);
public void FloorExitReached(); public void FloorExitReached();
public void NextFloorLoaded(); public void NextFloorLoaded();

View File

@@ -19,6 +19,8 @@ public partial class Player : CharacterBody3D, IPlayer
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
private PlayerLogic.IBinding PlayerBinding { get; set; } = default!; private PlayerLogic.IBinding PlayerBinding { get; set; } = default!;
[Dependency] private IGameRepo _gameRepo => this.DependOn<IGameRepo>();
#endregion #endregion
#region Save #region Save
@@ -233,22 +235,22 @@ public partial class Player : CharacterBody3D, IPlayer
var isAdded = Inventory.TryAdd(inventoryItem); var isAdded = Inventory.TryAdd(inventoryItem);
if (isAdded) if (isAdded)
{ {
Game.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up.");
inventoryItem.QueueFree(); inventoryItem.QueueFree();
} }
else else
Game.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}.");
} }
if (area.GetParent() is DroppedItem droppedItem) if (area.GetParent() is DroppedItem droppedItem)
{ {
var isAdded = Inventory.TryAdd(droppedItem.Item); var isAdded = Inventory.TryAdd(droppedItem.Item);
if (isAdded) if (isAdded)
{ {
Game.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up.");
droppedItem.QueueFree(); droppedItem.QueueFree();
} }
else else
Game.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}.");
} }
} }

View File

@@ -2,6 +2,8 @@ using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Renci.SshNet.Messages;
using Zennysoft.Game.Ma.Implementation;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
@@ -31,9 +33,34 @@ public partial class InGameUI : Control, IInGameUI
[Node] public IUseTeleportPrompt UseTeleportPrompt { get; set; } = default!; [Node] public IUseTeleportPrompt UseTeleportPrompt { get; set; } = default!;
[Dependency] private IGameRepo _gameRepo => this.DependOn<IGameRepo>();
public IInGameUILogic InGameUILogic { get; set; } = default!;
public InGameUILogic.IBinding InGameUILogicBinding { get; set; } = default!;
[Signal] [Signal]
public delegate void MinimapButtonReleasedEventHandler(); public delegate void MinimapButtonReleasedEventHandler();
public void Setup()
{
InGameUILogic = new InGameUILogic();
InGameUILogic.Set(_gameRepo);
}
public void OnResolved()
{
InGameUILogicBinding = InGameUILogic.Bind();
InGameUILogicBinding
.Handle((in InGameUILogic.Output.AnnounceMessage output) =>
{
PlayerInfoUI.DisplayMessage(output.Message);
});
InGameUILogic.Start();
}
public override void _UnhandledInput(InputEvent @event) public override void _UnhandledInput(InputEvent @event)
{ {
if (@event.IsActionReleased(GameInputs.MiniMap)) if (@event.IsActionReleased(GameInputs.MiniMap))