Overhaul game state logic to support gameplay loop

This commit is contained in:
2025-04-30 00:43:55 -07:00
parent 78cdda97b9
commit 68c91d8f13
60 changed files with 2503 additions and 1116 deletions

View File

@@ -31,13 +31,13 @@ public partial class Game : Node3D, IGame
public IInstantiator Instantiator { get; set; } = default!;
public IGameLogic GameLogic { get; set; } = default!;
public IGameState GameState { get; set; } = default!;
public IGameEventDepot GameEventDepot { get; set; } = default!;
public IGameRepo GameRepo { get; set; } = default!;
public GameLogic.IBinding GameBinding { get; set; } = default!;
public GameState.IBinding GameBinding { get; set; } = default!;
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
@@ -55,6 +55,8 @@ public partial class Game : Node3D, IGame
[Node] private Timer DoubleEXPTimer { get; set; } = default!;
[Node] private IPlayer Player { get; set; } = default!;
[Node] private MainMenu MainMenu { get; set; } = default!;
#endregion
#region Save
@@ -80,14 +82,14 @@ public partial class Game : Node3D, IGame
Module.Bootstrap(_container);
GameRepo = _container.GetInstance<IGameRepo>();
GameLogic = _container.GetInstance<IGameLogic>();
GameState = _container.GetInstance<IGameState>();
GameEventDepot = new GameEventDepot();
GameLogic.Set(GameRepo);
GameLogic.Set(AppRepo);
GameLogic.Set(GameEventDepot);
GameLogic.Set(Player);
GameLogic.Set(Map);
GameLogic.Set(InGameUI);
GameState.Set(GameRepo);
GameState.Set(AppRepo);
GameState.Set(GameEventDepot);
GameState.Set(Player);
GameState.Set(Map);
GameState.Set(InGameUI);
Instantiator = new Instantiator(GetTree());
RescuedItems = new RescuedItemDatabase();
@@ -96,43 +98,31 @@ public partial class Game : Node3D, IGame
{
var gameData = new GameData()
{
PlayerData = new PlayerData()
{
PlayerStats = new PlayerStats()
{
CurrentHP = Player.Stats.CurrentHP.Value,
MaximumHP = Player.Stats.MaximumHP.Value,
CurrentVT = Player.Stats.CurrentVT.Value,
MaximumVT = Player.Stats.MaximumVT.Value,
CurrentAttack = Player.Stats.CurrentAttack.Value,
BonusAttack = Player.Stats.BonusAttack.Value,
MaxAttack = Player.Stats.MaxAttack.Value,
CurrentDefense = Player.Stats.CurrentDefense.Value,
BonusDefense = Player.Stats.BonusDefense.Value,
MaxDefense = Player.Stats.MaxDefense.Value,
CurrentExp = Player.Stats.CurrentExp.Value,
CurrentLevel = Player.Stats.CurrentLevel.Value,
ExpToNextLevel = Player.Stats.ExpToNextLevel.Value,
Luck = Player.Stats.Luck.Value
},
PlayerStats = Player.Stats,
Inventory = Player.Inventory
},
MapData = new MapData()
{
FloorScenes = Map.FloorScenes
},
RescuedItems = new RescuedItemDatabase() { Items = RescuedItems.Items }
RescuedItems = new RescuedItemDatabase()
{
Items = RescuedItems.Items
}
};
return gameData;
},
onLoad: (chunk, data) =>
{
RescuedItems = data.RescuedItems;
chunk.LoadChunkSaveData(data.PlayerData);
chunk.LoadChunkSaveData(data.MapData);
}
);
onLoad:
(chunk, data) =>
{
RescuedItems = data.RescuedItems;
chunk.LoadChunkSaveData(data.PlayerData);
chunk.LoadChunkSaveData(data.MapData);
}
);
}
public void OnResolved()
@@ -157,30 +147,78 @@ public partial class Game : Node3D, IGame
}
);
GameBinding = GameLogic.Bind();
GameBinding = GameState.Bind();
GameBinding
.Handle((in GameLogic.Output.StartGame _) =>
.Handle((in GameState.Output.InitializeGame _) =>
{
InitializeGame();
Map.LoadMap();
GameRepo.Resume();
InGameUI.Show();
Player.InitializePlayerState();
InGameUI.PlayerInfoUI.Activate();
Player.TeleportPlayer(Map.CurrentFloor.GetPlayerSpawnPoint());
Player.Activate();
})
.Handle((in GameLogic.Output.GoToOverworld _) =>
.Handle((in GameState.Output.LoadGameFromFile _) =>
{
GameEventDepot.OnOverworldEntered();
LoadExistingGame();
})
.Handle((in GameLogic.Output.SetPauseMode output) => CallDeferred(nameof(SetPauseMode), output.IsPaused))
.Handle((in GameLogic.Output.ShowPauseMenu _) =>
.Handle((in GameState.Output.OpenPauseScreen _) =>
{
GameRepo.Pause();
PauseMenu.Show();
PauseMenu.FadeIn();
Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(true);
})
.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.LoadNextFloor _) =>
.Handle((in GameState.Output.ClosePauseScreen _) =>
{
PauseMenu.FadeOut();
Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(false);
GameRepo.Resume();
})
.Handle((in GameState.Output.OpenInventoryMenu _) =>
{
InGameUI.InventoryMenu.RefreshInventoryScreen();
InGameUI.InventoryMenu.Show();
InGameUI.InventoryMenu.SetProcessInput(true);
})
.Handle((in GameState.Output.CloseInventoryMenu _) =>
{
InGameUI.InventoryMenu.Hide();
InGameUI.InventoryMenu.SetProcessInput(false);
})
.Handle((in GameState.Output.OpenMiniMap _) =>
{
InGameUI.MiniMap.SetProcessUnhandledInput(true);
InGameUI.MiniMap.Show();
})
.Handle((in GameState.Output.CloseMiniMap _) =>
{
InGameUI.MiniMap.SetProcessUnhandledInput(false);
InGameUI.MiniMap.Hide();
})
.Handle((in GameState.Output.OpenDebugMenu _) =>
{
InGameUI.DebugMenu.Show();
InGameUI.PlayerInfoUI.Hide();
GameRepo.Pause();
})
.Handle((in GameState.Output.CloseDebugMenu _) =>
{
InGameUI.DebugMenu.Hide();
InGameUI.PlayerInfoUI.Show();
GameRepo.Resume();
})
.Handle((in GameState.Output.OpenFloorExitScreen _) =>
{
InGameUI.UseTeleportPrompt.Show();
InGameUI.UseTeleportPrompt.FadeIn();
})
.Handle((in GameState.Output.LoadNextFloor _) =>
{
InGameUI.UseTeleportPrompt.FadeOut();
Map.SpawnNextFloor();
if (Player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
{
@@ -200,59 +238,49 @@ public partial class Game : Node3D, IGame
Player.Unequip(itemToDestroy);
Player.Inventory.Remove(itemToDestroy);
}
FloorClearMenu.FadeOut();
})
.Handle((in GameLogic.Output.LoadMap _) => { Map.LoadMap(); })
.Handle((in GameLogic.Output.ShowFloorClearMenu _) => { FloorClearMenu.Show(); FloorClearMenu.FadeIn(); })
.Handle((in GameLogic.Output.ExitFloorClearMenu _) => { FloorClearMenu.FadeOut(); })
.Handle((in GameLogic.Output.ShowAskForTeleport _) => { GameRepo.Pause(); InGameUI.UseTeleportPrompt.FadeIn(); InGameUI.SetProcessInput(true); })
.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.ExitLostScreen _) => { DeathMenu.FadeOut(); })
.Handle((in GameLogic.Output.DoubleExpTimeStart output) => { DoubleEXPTimer.WaitTime = output.lengthOfTimeInSeconds; DoubleEXPTimer.Start(); })
.Handle((in GameLogic.Output.SaveGame _) =>
.Handle((in GameState.Output.GameOver _) =>
{
SaveFile.Save();
AppRepo.OnExitGame();
GetTree().Quit();
// Back to title screen
GameRepo.Pause();
DeathMenu.FadeIn();
});
GameLogic.Start();
GameLogic.Input(new GameLogic.Input.Initialize());
GameState.Start();
this.Provide();
PauseMenu.TransitionCompleted += OnPauseMenuTransitioned;
PauseMenu.UnpauseButtonPressed += PauseMenu_UnpauseButtonPressed;
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
FloorClearMenu.GoToNextFloor += FloorClearMenu_GoToNextFloor;
FloorClearMenu.SaveAndExit += FloorClearMenu_SaveAndExit;
FloorClearMenu.TransitionCompleted += FloorClearMenu_TransitionCompleted;
GameRepo.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout;
MainMenu.NewGame += OnNewGame;
MainMenu.LoadGame += OnLoadGame;
MainMenu.Quit += OnQuit;
DeathMenu.NewGame += OnContinueGame;
DeathMenu.QuitGame += OnQuit;
GameRepo.IsPaused.Sync += IsPaused_Sync;
_effectService = new EffectService(this, Player, Map);
MainMenu.Show();
}
public void LoadExistingGame() => SaveFile.Load().ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
public void TogglePause()
public void InitializeGame()
{
if (GameLogic.Value is GameLogic.State.Paused)
GameLogic.Input(new GameLogic.Input.UnpauseGame());
else
GameLogic.Input(new GameLogic.Input.PauseGame());
Map.InitializeMapData();
Player.InitializePlayerState();
}
public void FloorExitReached()
{
GameLogic.Input(new GameLogic.Input.AskForTeleport());
GameEventDepot.OnTeleportEntered();
GameState.Input(new GameState.Input.FloorExitEntered());
}
public async Task UseItem(InventoryItem item)
@@ -335,20 +363,15 @@ public partial class Game : Node3D, IGame
return rolledItem;
}
public void GameOver()
{
GameState.Input(new GameState.Input.GameOver());
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed(GameInputs.Debug) && !InGameUI.DebugMenu.Visible)
{
InGameUI.DebugMenu.Show();
InGameUI.PlayerInfoUI.Hide();
GameRepo.Pause();
}
else if (@event.IsActionPressed(GameInputs.Debug) && InGameUI.DebugMenu.Visible)
{
InGameUI.DebugMenu.Hide();
InGameUI.PlayerInfoUI.Show();
GameRepo.Resume();
}
if (@event.IsActionPressed(GameInputs.Debug))
GameState.Input(new GameState.Input.DebugButtonPressed());
}
private void DropRestorative(Vector3 vector)
@@ -359,49 +382,24 @@ public partial class Game : Node3D, IGame
restorative.GlobalPosition = vector;
}
private void UseTeleportPrompt_CloseTeleportPrompt() => GameLogic.Input(new GameLogic.Input.HideAskForTeleport());
private void UseTeleportPrompt_TeleportToNextFloor()
{
GameLogic.Input(new GameLogic.Input.FloorExitReached());
InGameUI.UseTeleportPrompt.FadeIn();
GameState.Input(new GameState.Input.LoadNextFloor());
GameEventDepot.OnDungeonAThemeAreaEntered();
}
private void PauseMenu_UnpauseButtonPressed() => GameLogic.Input(new GameLogic.Input.UnpauseGame());
private void Player_PauseButtonPressed() => GameLogic.Input(new GameLogic.Input.PauseGame());
private void FloorClearMenu_TransitionCompleted()
{
GameRepo.Resume();
if (Player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedWeapon.Value);
if (Player.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedArmor.Value);
if (Player.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
Player.Unequip(Player.EquippedAccessory.Value);
}
private void FloorClearMenu_GoToNextFloor() => GameLogic.Input(new GameLogic.Input.GoToNextFloor());
private void FloorClearMenu_SaveAndExit() => GameLogic.Input(new GameLogic.Input.SaveGame());
private void GameEventDepot_RestorativePickedUp(IHealthPack obj) => Player.Stats.SetCurrentVT(Player.Stats.CurrentVT.Value + (int)obj.RestoreAmount);
private void SetPauseMode(bool isPaused)
{
if (GetTree() != null)
GetTree().Paused = isPaused;
}
private void IsPaused_Sync(bool isPaused) => GetTree().Paused = isPaused;
private void DoubleEXPTimer_Timeout() => GameRepo.EndDoubleExp();
public void NextFloorLoaded() => GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
private void OnPauseMenuTransitioned() => GameLogic.Input(new GameLogic.Input.PauseMenuTransitioned());
public void OnStart() => GameLogic.Input(new GameLogic.Input.StartGame());
private void FinishedLoadingSaveFile() => EmitSignal(SignalName.SaveFileLoaded);
private void EnactConsumableItemEffects(ConsumableItem consumableItem)
@@ -498,4 +496,26 @@ public partial class Game : Node3D, IGame
else
GameRepo.RemoveItemFromInventory(item);
}
private void OnNewGame()
{
GameState.Input(new GameState.Input.NewGame());
MainMenu.Hide();
}
private void OnContinueGame()
{
GameState.Input(new GameState.Input.ContinueGame());
}
private void OnLoadGame()
{
GameState.Input(new GameState.Input.LoadGame());
MainMenu.Hide();
}
private void OnQuit()
{
MainMenu.Hide();
}
}