Move files and folders to new repo format to enable multi-project format

This commit is contained in:
2025-03-06 22:07:25 -08:00
parent 12cbb82ac9
commit a09f6ec5a5
3973 changed files with 1781 additions and 2938 deletions

View File

@@ -0,0 +1,30 @@
using DialogueManagerRuntime;
using Godot;
namespace Zennysoft.Game.Ma;
public partial class DialogueController : Node
{
public static PackedScene DialogueBalloon { get; set; }
private static Node _currentlyActiveDialogue { get; set; } = new Node();
private static Resource _currentDialogue { get; set; }
public override void _Ready()
{
DialogueBalloon = GD.Load<PackedScene>("res://src/ui/dialogue/DialogueBalloon.tscn");
}
public static void ShowDialogue(Resource dialogueResource, string dialogueTitle)
{
Interrupt();
_currentlyActiveDialogue = DialogueManager.ShowDialogueBalloonScene(DialogueBalloon, dialogueResource, dialogueTitle);
}
public static void Interrupt()
{
if (IsInstanceValid(_currentlyActiveDialogue))
_currentlyActiveDialogue.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Zennysoft.Game.Ma;
public enum ElementType
{
None,
Aeolic,
Telluric,
Hydric,
Igneous,
Ferrum
}
[JsonSerializable(typeof(ElementType))]
public partial class ElementTypeEnumContext : JsonSerializerContext;

View File

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

View File

@@ -0,0 +1,493 @@
namespace Zennysoft.Game.Ma;
using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Chickensoft.Serialization;
using Chickensoft.Serialization.Godot;
using Zennysoft.Game.Ma.src.items;
using Godot;
using System;
using System.IO.Abstractions;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using static Zennysoft.Game.Ma.GameLogic.State;
[Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame
{
public override void _Notification(int what) => this.Notify(what);
IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
IGame IProvide<IGame>.Value() => this;
IPlayer IProvide<IPlayer>.Value() => Player;
IGameEventDepot IProvide<IGameEventDepot>.Value() => GameEventDepot;
public IInstantiator Instantiator { get; set; } = default!;
public IGameLogic GameLogic { get; set; } = default!;
public IGameEventDepot GameEventDepot { get; set; } = default!;
public IGameRepo GameRepo { get; set; } = default!;
public GameLogic.IBinding GameBinding { get; set; } = default!;
[Signal]
public delegate void StatRaisedAlertEventHandler(string statRaisedAlert);
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
#region Nodes
[Node] private IMap Map { get; set; } = default!;
[Node] private InGameUI InGameUI { get; set; } = default!;
[Node] private IFloorClearMenu FloorClearMenu { get; set; } = default!;
[Node] private DeathMenu DeathMenu { get; set; } = default!;
[Node] private IPauseMenu PauseMenu { get; set; } = default!;
[Node] private InGameAudio InGameAudio { get; set; } = default!;
[Node] private Timer DoubleEXPTimer { get; set; } = default!;
[Node] private IPlayer Player { get; set; } = default!;
#endregion
#region Save
public JsonSerializerOptions JsonOptions { get; set; } = default!;
public const string SAVE_FILE_NAME = "game.json";
public IFileSystem FileSystem { get; set; } = default!;
public string SaveFilePath { get; set; } = default!;
public ISaveFile<GameData> SaveFile { get; set; } = default!;
public ISaveChunk<GameData> GameChunk { get; set; } = default!;
ISaveChunk<GameData> IProvide<ISaveChunk<GameData>>.Value() => GameChunk;
[Signal]
public delegate void SaveFileLoadedEventHandler();
#endregion
public RescuedItemDatabase RescuedItems { get; set; } = default!;
private EffectService _effectService;
public void Setup()
{
FileSystem = new FileSystem();
SaveFilePath = FileSystem.Path.Join(OS.GetUserDataDir(), SAVE_FILE_NAME);
GameRepo = new GameRepo();
GameLogic = new GameLogic();
GameEventDepot = new GameEventDepot();
GameLogic.Set(GameRepo);
GameLogic.Set(AppRepo);
GameLogic.Set(GameEventDepot);
GameLogic.Set(Player);
GameLogic.Set(Map);
Instantiator = new Instantiator(GetTree());
RescuedItems = new RescuedItemDatabase();
var resolver = new SerializableTypeResolver();
GodotSerialization.Setup();
Serializer.AddConverter(new Texture2DConverter());
var upgradeDependencies = new Blackboard();
JsonOptions = new JsonSerializerOptions
{
Converters = {
new SerializableTypeConverter(upgradeDependencies)
},
TypeInfoResolver = JsonTypeInfoResolver.Combine(resolver, WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default),
WriteIndented = true
};
GameChunk = new SaveChunk<GameData>(
(chunk) =>
{
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
},
Inventory = Player.Inventory
},
MapData = new MapData()
{
FloorScenes = Map.FloorScenes
},
RescuedItems = new RescuedItemDatabase() { Items = RescuedItems.Items }
};
return gameData;
},
onLoad: (chunk, data) =>
{
RescuedItems = data.RescuedItems;
chunk.LoadChunkSaveData(data.PlayerData);
chunk.LoadChunkSaveData(data.MapData);
}
);
}
public void OnResolved()
{
SaveFile = new SaveFile<GameData>(
root: GameChunk,
onSave: async (GameData data) =>
{
// Save the game data to disk.
var json = JsonSerializer.Serialize(data, JsonOptions);
await FileSystem.File.WriteAllTextAsync(SaveFilePath, json);
},
onLoad: async () =>
{
// Load the game data from disk.
if (!FileSystem.File.Exists(SaveFilePath))
{
GD.Print("No save file to load");
return null;
}
var json = await FileSystem.File.ReadAllTextAsync(SaveFilePath);
return JsonSerializer.Deserialize<GameData>(json, JsonOptions);
}
);
GameBinding = GameLogic.Bind();
GameBinding
.Handle((in GameLogic.Output.StartGame _) =>
{
GameRepo.Resume();
InGameUI.Show();
})
.Handle((in GameLogic.Output.GoToOverworld _) =>
{
GameEventDepot.OnOverworldEntered();
})
.Handle((in GameLogic.Output.SetPauseMode output) => CallDeferred(nameof(SetPauseMode), output.IsPaused))
.Handle((in GameLogic.Output.ShowPauseMenu _) =>
{
PauseMenu.Show();
PauseMenu.FadeIn();
Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(true);
})
.Handle((in GameLogic.Output.LoadNextFloor _) => { Map.SpawnNextFloor(); })
.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.ShowFloorClearMenu _) => { FloorClearMenu.Show(); FloorClearMenu.FadeIn(); })
.Handle((in GameLogic.Output.ExitFloorClearMenu _) => { FloorClearMenu.FadeOut(); })
.Handle((in GameLogic.Output.OpenInventory _) => { InGameUI.ShowInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(true); })
.Handle((in GameLogic.Output.HideInventory _) => { InGameUI.HideInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(false); })
.Handle((in GameLogic.Output.ShowMiniMap _) => { InGameUI.ShowMiniMap(); })
.Handle((in GameLogic.Output.HideMiniMap _) => { InGameUI.HideMiniMap(); })
.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.SaveGame _) =>
{
SaveFile.Save();
AppRepo.OnExitGame();
GetTree().Quit();
// Back to title screen
});
GameLogic.Start();
GameLogic.Input(new GameLogic.Input.Initialize());
this.Provide();
PauseMenu.TransitionCompleted += OnPauseMenuTransitioned;
PauseMenu.UnpauseButtonPressed += PauseMenu_UnpauseButtonPressed;
InGameUI.InventoryMenu.ClosedMenu += InventoryMenu_CloseInventory;
InGameUI.MinimapButtonReleased += Player_MinimapButtonReleased;
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
FloorClearMenu.GoToNextFloor += FloorClearMenu_GoToNextFloor;
FloorClearMenu.SaveAndExit += FloorClearMenu_SaveAndExit;
FloorClearMenu.TransitionCompleted += FloorClearMenu_TransitionCompleted;
GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout;
_effectService = new EffectService(this, Player);
}
public void LoadExistingGame()
{
SaveFile.Load()
.ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
}
public void TogglePause()
{
if (GameLogic.Value is Paused)
GameLogic.Input(new GameLogic.Input.UnpauseGame());
else
GameLogic.Input(new GameLogic.Input.PauseGame());
}
public void ToggleInventory()
{
if (GameLogic.Value is InventoryOpened)
GameLogic.Input(new GameLogic.Input.CloseInventory());
else
GameLogic.Input(new GameLogic.Input.OpenInventory());
}
public void ToggleMinimap()
{
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
}
public void FloorExitReached()
{
GameLogic.Input(new GameLogic.Input.AskForTeleport());
GameEventDepot.OnTeleportEntered();
}
public void UseItem(InventoryItem item)
{
if (item is ConsumableItem consumableItem)
{
if (Player.Stats.CurrentHP == Player.Stats.MaximumHP && consumableItem.RaiseHPAmount > 0)
Player.RaiseHP(consumableItem.RaiseHPAmount);
if (Player.Stats.CurrentVT == Player.Stats.MaximumVT && consumableItem.RaiseVTAmount > 0)
Player.RaiseVT(consumableItem.RaiseVTAmount);
if (consumableItem.HealHPAmount > 0 && Player.Stats.CurrentHP != Player.Stats.MaximumHP)
Player.HealHP(consumableItem.HealHPAmount);
if (consumableItem.HealVTAmount > 0 && Player.Stats.CurrentVT != Player.Stats.MaximumVT)
Player.HealVT(consumableItem.HealVTAmount);
}
if (item is EffectItem effectItem)
{
switch (effectItem.UsableItemTag)
{
case UsableItemTag.DoubleEXP:
DoubleEXP(TimeSpan.FromSeconds(30));
break;
case UsableItemTag.TeleportAllEnemiesToRoom:
_effectService.TeleportEnemiesToCurrentRoom();
break;
case UsableItemTag.KillHalfEnemiesInRoom:
_effectService.KillHalfEnemiesInRoom();
break;
case UsableItemTag.TurnAllEnemiesIntoHealingItem:
_effectService.TurnAllEnemiesInRoomIntoHealingItem();
break;
case UsableItemTag.HealsAllInRoomToMaxHP:
_effectService.HealAllEnemiesAndPlayerInRoomToFull();
break;
case UsableItemTag.AbsorbHPFromAllEnemiesInRoom:
_effectService.AbsorbHPFromAllEnemiesInRoom();
break;
case UsableItemTag.DealElementalDamageToAllEnemiesInRoom:
_effectService.DealElementalDamageToAllEnemiesInRoom(ElementType.Hydric);
break;
case UsableItemTag.SwapHPAndVT:
_effectService.SwapHPandVT();
break;
case UsableItemTag.RaiseCurrentWeaponAttack:
_effectService.RaiseCurrentWeaponAttack();
break;
case UsableItemTag.RaiseCurrentDefenseArmor:
_effectService.RaiseCurrentArmorDefense();
break;
case UsableItemTag.RaiseLevel:
_effectService.RaiseLevel();
break;
case UsableItemTag.RandomEffect:
_effectService.RandomEffect(effectItem);
break;
}
}
if (item is ThrowableItem throwableItem)
{
if (throwableItem.HealHPAmount > 0)
Player.HealHP(throwableItem.HealHPAmount);
if (throwableItem.HealVTAmount > 0)
Player.HealVT(throwableItem.HealVTAmount);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.TeleportToRandomLocation)
_effectService.TeleportToRandomRoom(Player);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.CanChangeAffinity)
_effectService.ChangeAffinity(throwableItem);
if (throwableItem.ThrowableItemTag == ThrowableItemTag.WarpToExitIfFound)
_effectService.WarpToExit(Player);
}
}
public void DropItem(InventoryItem item)
{
var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn");
var dropped = droppedScene.Instantiate<DroppedItem>();
dropped.Item = item;
AddChild(dropped);
dropped.Drop();
}
public void ThrowItem(InventoryItem item)
{
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
var thrown = thrownScene.Instantiate<ThrownItem>();
thrown.ItemThatIsThrown = item;
AddChild(thrown);
thrown.Position += new Vector3(0, 1.5f, 0);
thrown.Throw(_effectService);
}
public void AnnounceMessageOnInventoryScreen(string message)
{
InGameUI.InventoryMenu.ShowMessage(message);
}
public void AnnounceMessageOnMainScreen(string message)
{
InGameUI.PlayerInfoUI.DisplayMessage(message);
}
public IDungeonFloor CurrentFloor => Map.CurrentFloor;
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource)
{
Player.GainExp(resource.ExpFromDefeat * GameRepo.EXPRate);
}
private void DropRestorative(Vector3 vector)
{
var restorativeScene = GD.Load<PackedScene>("res://src/items/restorative/Restorative.tscn");
var restorative = restorativeScene.Instantiate<Restorative>();
AddChild(restorative);
restorative.GlobalPosition = vector;
}
private void UseTeleportPrompt_CloseTeleportPrompt()
{
GameLogic.Input(new GameLogic.Input.HideAskForTeleport());
}
private void UseTeleportPrompt_TeleportToNextFloor()
{
GameLogic.Input(new GameLogic.Input.FloorExitReached());
GameEventDepot.OnDungeonAThemeAreaEntered();
}
private void PauseMenu_UnpauseButtonPressed()
{
GameLogic.Input(new GameLogic.Input.UnpauseGame());
}
private void Player_PauseButtonPressed()
{
GameLogic.Input(new GameLogic.Input.PauseGame());
}
private void Player_MinimapButtonReleased()
{
GameLogic.Input(new GameLogic.Input.MiniMapButtonReleased());
}
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()
{
// Save
GameLogic.Input(new GameLogic.Input.SaveGame());
}
private void GameEventDepot_RestorativePickedUp(Restorative obj)
=> Player.Stats.SetCurrentVT(Player.Stats.CurrentVT.Value + obj.VTRestoreAmount);
private void SetPauseMode(bool isPaused)
{
if (GetTree() != null)
GetTree().Paused = isPaused;
}
public async void DoubleEXP(TimeSpan lengthOfEffect)
{
ToggleInventory();
AnnounceMessageOnMainScreen("Experience points temporarily doubled.");
DoubleEXPTimer.Start(lengthOfEffect.Seconds);
GameRepo.EXPRate = 2;
}
private void DoubleEXPTimer_Timeout()
{
DoubleEXPTimer.Stop();
GameRepo.EXPRate = 1;
AnnounceMessageOnMainScreen("Experience points effect wore off.");
}
private void InventoryMenu_CloseInventory() => GameLogic.Input(new GameLogic.Input.CloseInventory());
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);
}
}

View File

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

View File

@@ -0,0 +1,86 @@
[gd_scene load_steps=13 format=3 uid="uid://33ek675mfb5n"]
[ext_resource type="Script" uid="uid://chftlu4proh3d" path="res://src/game/Game.cs" id="1_ytcii"]
[ext_resource type="Shader" uid="uid://dmjxo4k2rx1an" path="res://src/app/App.gdshader" id="2_6ifxs"]
[ext_resource type="PackedScene" uid="uid://by67pn7fdsg1m" path="res://src/map/Map.tscn" id="3_d8awv"]
[ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="3_kk6ly"]
[ext_resource type="PackedScene" uid="uid://b1muxus5qdbeu" path="res://src/ui/in_game_ui/InGameUI.tscn" id="5_lxtnp"]
[ext_resource type="PackedScene" uid="uid://b16ejcwanod72" path="res://src/audio/InGameAudio.tscn" id="6_qc71l"]
[ext_resource type="Script" uid="uid://daphxl6vvsbjm" path="res://src/game/DialogueController.cs" id="10_58pbt"]
[ext_resource type="Script" uid="uid://cbal5oeaha4nx" path="res://src/ui/pause_menu/PauseMenu.cs" id="11_5ng8c"]
[ext_resource type="PackedScene" uid="uid://pu6gp8de3ck4" path="res://src/ui/floor_clear/FloorClearMenu.tscn" id="11_rya1n"]
[ext_resource type="PackedScene" uid="uid://dbtfgrtgpr4qg" path="res://src/ui/death_menu/DeathMenu.tscn" id="11_wypid"]
[ext_resource type="PackedScene" uid="uid://blbqgw3wosc1w" path="res://src/ui/pause_menu/PauseMenu.tscn" id="12_yev8k"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_e75a2"]
shader = ExtResource("2_6ifxs")
shader_parameter/change_color_depth = false
shader_parameter/target_color_depth = 7
shader_parameter/dithering = false
shader_parameter/scale_resolution = false
shader_parameter/target_resolution_scale = 4
shader_parameter/enable_recolor = false
[node name="Game" type="Node3D"]
process_mode = 3
script = ExtResource("1_ytcii")
[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
material = SubResource("ShaderMaterial_e75a2")
custom_minimum_size = Vector2(1280, 960)
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
stretch = true
[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"]
transparent_bg = true
handle_input_locally = false
size = Vector2i(1280, 960)
render_target_update_mode = 4
[node name="PauseContainer" type="Node3D" parent="SubViewportContainer/SubViewport"]
unique_name_in_owner = true
process_mode = 1
[node name="Player" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_kk6ly")]
unique_name_in_owner = true
process_mode = 1
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0, -3, 0)
[node name="Map" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_d8awv")]
unique_name_in_owner = true
process_mode = 1
[node name="StatusEffectTimers" type="Node" parent="SubViewportContainer/SubViewport/PauseContainer"]
[node name="DoubleEXPTimer" type="Timer" parent="SubViewportContainer/SubViewport/PauseContainer/StatusEffectTimers"]
unique_name_in_owner = true
wait_time = 30.0
[node name="InGameUI" parent="." instance=ExtResource("5_lxtnp")]
unique_name_in_owner = true
visible = false
[node name="InGameAudio" parent="." instance=ExtResource("6_qc71l")]
unique_name_in_owner = true
[node name="DialogueController" type="Node" parent="."]
unique_name_in_owner = true
process_mode = 3
script = ExtResource("10_58pbt")
[node name="DeathMenu" parent="." instance=ExtResource("11_wypid")]
unique_name_in_owner = true
visible = false
[node name="FloorClearMenu" parent="." instance=ExtResource("11_rya1n")]
unique_name_in_owner = true
visible = false
[node name="PauseMenu" parent="." instance=ExtResource("12_yev8k")]
unique_name_in_owner = true
visible = false
script = ExtResource("11_5ng8c")

View File

@@ -0,0 +1,17 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace Zennysoft.Game.Ma;
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
[Save("map_data")]
public required MapData MapData { get; init; }
[Save("rescued_items")]
public required RescuedItemDatabase RescuedItems { get; init; }
}

View File

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

View File

@@ -0,0 +1,80 @@
using Godot;
using System;
namespace Zennysoft.Game.Ma;
public interface IGameEventDepot : IDisposable
{
event Action? OverworldEntered;
public void OnOverworldEntered();
event Action? DungeonAThemeAreaEntered;
public void OnDungeonAThemeAreaEntered();
event Action? DungeonBThemeAreaEntered;
public void OnDungeonBThemeAreaEntered();
event Action? DungeonCThemeAreaEntered;
public void OnDungeonCThemeAreaEntered();
event Action? TeleportEntered;
public void OnTeleportEntered();
event Action? MenuScrolled;
public void OnMenuScrolled();
event Action? MenuBackedOut;
public void OnMenuBackedOut();
event Action? InventorySorted;
public void OnInventorySorted();
event Action<ConsumableItemStats>? HealingItemConsumed;
public void OnHealingItemConsumed(ConsumableItemStats item);
event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource);
event Action<Restorative>? RestorativePickedUp;
public void OnRestorativePickedUp(Restorative restorative);
}
public class GameEventDepot : IGameEventDepot
{
public event Action? OverworldEntered;
public event Action? DungeonAThemeAreaEntered;
public event Action? DungeonBThemeAreaEntered;
public event Action? DungeonCThemeAreaEntered;
public event Action? TeleportEntered;
public event Action? MenuScrolled;
public event Action? MenuBackedOut;
public event Action? InventorySorted;
public event Action<ConsumableItemStats>? HealingItemConsumed;
public event Action<Restorative>? RestorativePickedUp;
public event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnOverworldEntered() => OverworldEntered?.Invoke();
public void OnDungeonAThemeAreaEntered() => DungeonAThemeAreaEntered?.Invoke();
public void OnDungeonBThemeAreaEntered() => DungeonBThemeAreaEntered?.Invoke();
public void OnDungeonCThemeAreaEntered() => DungeonCThemeAreaEntered?.Invoke();
public void OnTeleportEntered() => TeleportEntered?.Invoke();
public void OnMenuScrolled() => MenuScrolled?.Invoke();
public void OnMenuBackedOut() => MenuBackedOut?.Invoke();
public void OnInventorySorted() => InventorySorted?.Invoke();
public void OnHealingItemConsumed(ConsumableItemStats item) => HealingItemConsumed?.Invoke(item);
public void OnRestorativePickedUp(Restorative restorative) => RestorativePickedUp?.Invoke(restorative);
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource) => EnemyDefeated?.Invoke(position, enemyStatResource);
public void Dispose()
{
GC.SuppressFinalize(this);
}
}

View File

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

View File

@@ -0,0 +1,40 @@
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public static class Input
{
public readonly record struct StartGame;
public readonly record struct Initialize;
public readonly record struct GoToOverworld;
public readonly record struct SaveGame;
public readonly record struct OpenInventory;
public readonly record struct CloseInventory;
public readonly record struct MiniMapButtonPressed;
public readonly record struct MiniMapButtonReleased;
public readonly record struct FloorExitReached;
public readonly record struct HideFloorClearMenu;
public readonly record struct GameOver;
public readonly record struct GoToNextFloor;
public readonly record struct PauseGame;
public readonly record struct UnpauseGame;
public readonly record struct PauseMenuTransitioned;
public readonly record struct AskForTeleport;
public readonly record struct HideAskForTeleport;
}
}

View File

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

View File

@@ -0,0 +1,43 @@
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public static class Output
{
public readonly record struct StartGame;
public readonly record struct ShowPauseMenu;
public readonly record struct HidePauseMenu;
public readonly record struct ExitPauseMenu;
public readonly record struct OpenInventory();
public readonly record struct HideInventory;
public readonly record struct SetPauseMode(bool IsPaused);
public readonly record struct ShowMiniMap;
public readonly record struct HideMiniMap;
public readonly record struct ShowLostScreen;
public readonly record struct ExitLostScreen;
public readonly record struct LoadNextFloor;
public readonly record struct ShowFloorClearMenu;
public readonly record struct ExitFloorClearMenu;
public readonly record struct ShowAskForTeleport;
public readonly record struct HideAskForTeleport;
public readonly record struct GoToOverworld;
public readonly record struct SaveGame;
}
}

View File

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

View File

@@ -0,0 +1,27 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
[Meta]
public abstract partial record State : StateLogic<State>
{
protected State()
{
OnAttach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync += OnIsPaused;
});
OnDetach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync -= OnIsPaused;
});
}
public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused));
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,48 @@
@startuml GameLogic
state "GameLogic State" as Zennysoft_Game_Ma_GameLogic_State {
state "MenuBackdrop" as Zennysoft_Game_Ma_GameLogic_State_MenuBackdrop
state "Playing" as Zennysoft_Game_Ma_GameLogic_State_Playing {
state "AskForTeleport" as Zennysoft_Game_Ma_GameLogic_State_AskForTeleport
state "FloorClearedDecisionState" as Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState
state "InventoryOpened" as Zennysoft_Game_Ma_GameLogic_State_InventoryOpened
state "MinimapOpen" as Zennysoft_Game_Ma_GameLogic_State_MinimapOpen
state "Paused" as Zennysoft_Game_Ma_GameLogic_State_Paused
state "Resuming" as Zennysoft_Game_Ma_GameLogic_State_Resuming
}
state "Quit" as Zennysoft_Game_Ma_GameLogic_State_Quit
}
Zennysoft_Game_Ma_GameLogic_State_AskForTeleport --> Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState : FloorExitReached
Zennysoft_Game_Ma_GameLogic_State_AskForTeleport --> Zennysoft_Game_Ma_GameLogic_State_Playing : HideAskForTeleport
Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState --> Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState : GoToNextFloor
Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState --> Zennysoft_Game_Ma_GameLogic_State_Playing : HideFloorClearMenu
Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState --> Zennysoft_Game_Ma_GameLogic_State_Playing : SaveGame
Zennysoft_Game_Ma_GameLogic_State_InventoryOpened --> Zennysoft_Game_Ma_GameLogic_State_Playing : CloseInventory
Zennysoft_Game_Ma_GameLogic_State_MenuBackdrop --> Zennysoft_Game_Ma_GameLogic_State_MenuBackdrop : Initialize
Zennysoft_Game_Ma_GameLogic_State_MenuBackdrop --> Zennysoft_Game_Ma_GameLogic_State_Playing : StartGame
Zennysoft_Game_Ma_GameLogic_State_MinimapOpen --> Zennysoft_Game_Ma_GameLogic_State_Playing : MiniMapButtonReleased
Zennysoft_Game_Ma_GameLogic_State_Paused --> Zennysoft_Game_Ma_GameLogic_State_Resuming : UnpauseGame
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_AskForTeleport : AskForTeleport
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_InventoryOpened : OpenInventory
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_MinimapOpen : MiniMapButtonPressed
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_Paused : PauseGame
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_Playing : GoToOverworld
Zennysoft_Game_Ma_GameLogic_State_Playing --> Zennysoft_Game_Ma_GameLogic_State_Quit : GameOver
Zennysoft_Game_Ma_GameLogic_State_Resuming --> Zennysoft_Game_Ma_GameLogic_State_Playing : PauseMenuTransitioned
Zennysoft_Game_Ma_GameLogic_State : OnIsPaused() → SetPauseMode
Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState : OnGoToNextFloor → LoadNextFloor
Zennysoft_Game_Ma_GameLogic_State_FloorClearedDecisionState : OnSaveGame → SaveGame
Zennysoft_Game_Ma_GameLogic_State_InventoryOpened : OnEnter → OpenInventory
Zennysoft_Game_Ma_GameLogic_State_InventoryOpened : OnExit → HideInventory
Zennysoft_Game_Ma_GameLogic_State_MinimapOpen : OnEnter → ShowMiniMap
Zennysoft_Game_Ma_GameLogic_State_MinimapOpen : OnExit → HideMiniMap
Zennysoft_Game_Ma_GameLogic_State_Paused : OnEnter → ShowPauseMenu
Zennysoft_Game_Ma_GameLogic_State_Paused : OnExit → ExitPauseMenu
Zennysoft_Game_Ma_GameLogic_State_Playing : None → StartGame
Zennysoft_Game_Ma_GameLogic_State_Playing : OnGoToOverworld → GoToOverworld
Zennysoft_Game_Ma_GameLogic_State_Quit : OnEnter → ShowLostScreen
Zennysoft_Game_Ma_GameLogic_State_Resuming : OnExit → HidePauseMenu
[*] --> Zennysoft_Game_Ma_GameLogic_State_MenuBackdrop
@enduml

View File

@@ -0,0 +1,87 @@
using Chickensoft.Collections;
using Godot;
using System;
namespace Zennysoft.Game.Ma;
public interface IGameRepo : IDisposable
{
void Pause();
void Resume();
IAutoProp<bool> IsPaused { get; }
void SetPlayerGlobalTransform(Transform3D playerGlobalTransform);
public int MaxItemSize { get; }
public int EXPRate { get; set; }
public int CurrentFloor { get; set; }
}
public class GameRepo : IGameRepo
{
public event Action? Ended;
public IAutoProp<Transform3D> PlayerGlobalTransform => _playerGlobalTransform;
private readonly AutoProp<Transform3D> _playerGlobalTransform;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
public int MaxItemSize => 20;
public int EXPRate { get; set; } = 1;
private bool _disposedValue;
public int CurrentFloor { get; set; } = 0;
public GameRepo()
{
_isPaused = new AutoProp<bool>(true);
_playerGlobalTransform = new AutoProp<Transform3D>(Transform3D.Identity);
}
public void Pause()
{
_isPaused.OnNext(true);
GD.Print("Paused");
}
public void Resume()
{
_isPaused.OnNext(false);
GD.Print("Resume");
}
public void SetPlayerGlobalTransform(Transform3D playerGlobalTransform) => _playerGlobalTransform.OnNext(playerGlobalTransform);
public void OnGameEnded()
{
Pause();
Ended?.Invoke();
}
protected void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_isPaused.OnCompleted();
_isPaused.Dispose();
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

View File

@@ -0,0 +1 @@
uid://2n7h2jneaihr

View File

@@ -0,0 +1,45 @@
namespace Zennysoft.Game.Ma;
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder;
using Godot;
using System;
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, IProvide<ISaveChunk<GameData>>, INode3D
{
void LoadExistingGame();
event Game.SaveFileLoadedEventHandler? SaveFileLoaded;
event Game.StatRaisedAlertEventHandler StatRaisedAlert;
public RescuedItemDatabase RescuedItems { get; }
public IDungeonFloor CurrentFloor { get; }
public void UseItem(InventoryItem item);
public void DropItem(InventoryItem item);
public void ThrowItem(InventoryItem item);
public void DoubleEXP(TimeSpan lengthOfEffect);
public void ToggleInventory();
public void ToggleMinimap();
public void AnnounceMessageOnInventoryScreen(string message);
public void AnnounceMessageOnMainScreen(string message);
public void FloorExitReached();
public void NextFloorLoaded();
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource enemyStatResource);
public void TogglePause();
}

View File

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

View File

@@ -0,0 +1,26 @@
using Chickensoft.Introspection;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record AskForTeleport : Playing, IGet<Input.FloorExitReached>, IGet<Input.HideAskForTeleport>
{
public AskForTeleport()
{
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowAskForTeleport()); });
this.OnDetach(() => { Output(new Output.HideAskForTeleport()); });
}
public Transition On(in Input.FloorExitReached input) => To<FloorClearedDecisionState>();
public Transition On(in Input.HideAskForTeleport input)
{
return To<Playing>();
}
}
}
}

View File

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

View File

@@ -0,0 +1,36 @@
using Chickensoft.Introspection;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record FloorClearedDecisionState : Playing, IGet<Input.GoToNextFloor>, IGet<Input.SaveGame>, IGet<Input.HideFloorClearMenu>
{
public FloorClearedDecisionState()
{
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowFloorClearMenu()); });
this.OnDetach(() => { Output(new Output.ExitFloorClearMenu()); });
}
public Transition On(in Input.GoToNextFloor input)
{
Output(new Output.LoadNextFloor());
return ToSelf();
}
public Transition On(in Input.HideFloorClearMenu input)
{
return To<Playing>();
}
public Transition On(in Input.SaveGame input)
{
Output(new Output.SaveGame());
return To<Playing>();
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record InventoryOpened : Playing, IGet<Input.CloseInventory>
{
public InventoryOpened()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.OpenInventory()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideInventory()); });
}
public Transition On(in Input.CloseInventory input)
{
return To<Playing>();
}
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
namespace Zennysoft.Game.Ma;
using Chickensoft.Introspection;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record MenuBackdrop : State, IGet<Input.StartGame>, IGet<Input.Initialize>
{
public MenuBackdrop()
{
OnAttach(() => Get<IAppRepo>().GameEntered += OnGameEntered);
OnDetach(() => Get<IAppRepo>().GameEntered -= OnGameEntered);
}
public void OnGameEntered() => Input(new Input.StartGame());
public Transition On(in Input.StartGame input)
{
Get<IMap>().LoadMap();
return To<Playing>();
}
public Transition On(in Input.Initialize input)
{
return ToSelf();
}
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record MinimapOpen : Playing, IGet<Input.MiniMapButtonReleased>
{
public MinimapOpen()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowMiniMap()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideMiniMap()); });
}
public Transition On(in Input.MiniMapButtonReleased input) => To<Playing>();
}
}
}

View File

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

View File

@@ -0,0 +1,26 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Paused : Playing, IGet<Input.UnpauseGame>
{
public Paused()
{
this.OnEnter(() =>
{
Get<IGameRepo>().Pause();
Output(new Output.ShowPauseMenu());
});
this.OnExit(() => Output(new Output.ExitPauseMenu()));
}
public virtual Transition On(in Input.UnpauseGame input) => To<Resuming>();
}
}
}

View File

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

View File

@@ -0,0 +1,43 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Playing : State,
IGet<Input.OpenInventory>,
IGet<Input.MiniMapButtonPressed>,
IGet<Input.GameOver>,
IGet<Input.AskForTeleport>,
IGet<Input.PauseGame>,
IGet<Input.GoToOverworld>
{
public Playing()
{
OnAttach(() => Output(new Output.StartGame()));
}
public void OnEnded() => Input(new Input.GameOver());
public Transition On(in Input.OpenInventory input) => To<InventoryOpened>();
public Transition On(in Input.MiniMapButtonPressed input) => To<MinimapOpen>();
public Transition On(in Input.GameOver input) => To<Quit>();
public Transition On(in Input.AskForTeleport input) => To<AskForTeleport>();
public Transition On(in Input.PauseGame input) => To<Paused>();
public Transition On(in Input.GoToOverworld input)
{
Output(new Output.GoToOverworld());
return ToSelf();
}
}
}
}

View File

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

View File

@@ -0,0 +1,19 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Game.Ma;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Quit : State
{
public Quit()
{
this.OnEnter(() => Output(new Output.ShowLostScreen()));
}
}
}
}

View File

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

View File

@@ -0,0 +1,23 @@
namespace Zennysoft.Game.Ma;
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Resuming : Playing, IGet<Input.PauseMenuTransitioned>
{
public Resuming()
{
this.OnEnter(() => Get<IGameRepo>().Resume());
this.OnExit(() => Output(new Output.HidePauseMenu()));
}
public Transition On(in Input.PauseMenuTransitioned input) =>
To<Playing>();
}
}
}

View File

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