Attempt to bring in other branch

This commit is contained in:
2024-09-18 16:51:27 -07:00
1389 changed files with 64824 additions and 2665 deletions

View File

@@ -0,0 +1,28 @@
using DialogueManagerRuntime;
using Godot;
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

@@ -1,11 +1,19 @@
namespace GameJamDungeon;
using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using System;
public interface IGame : IProvide<IGameRepo>, INode3D;
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, INode3D
{
event Game.StatRaisedAlertEventHandler StatRaisedAlert;
public IPlayer Player { get; }
}
[Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame
@@ -14,19 +22,40 @@ public partial class Game : Node3D, IGame
IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
IGame IProvide<IGame>.Value() => this;
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>();
[Node] public IInventoryMenu InventoryMenu { get; set; } = default!;
#region Nodes
[Node] public IMap Map { get; set; } = default!;
[Node] public Control MiniMap { get; set; } = default!;
[Node] public IPlayer Player { get; set; } = default!;
[Node] public NavigationRegion3D NavigationRegion { get; set; } = default!;
[Node] public InGameUI InGameUI { get; set; } = default!;
[Node] public IFloorClearMenu FloorClearMenu { get; set; } = default!;
[Node] public DeathMenu DeathMenu { get; set; } = default!;
[Node] public IPauseMenu PauseMenu { get; set; } = default!;
[Node] public InGameAudio InGameAudio { get; set; } = default!;
#endregion
public const string SECOND_FLOOR_PATH = "res://src/map/dungeon/floors/SecondFloor.tscn";
public const string THIRD_FLOOR_PATH = "res://src/map/dungeon/floors/ThirdFloor.tscn";
@@ -35,47 +64,174 @@ public partial class Game : Node3D, IGame
{
GameRepo = new GameRepo();
GameLogic = new GameLogic();
GameEventDepot = new GameEventDepot();
GameLogic.Set(GameRepo);
GameLogic.Set(AppRepo);
GameLogic.Set(GameEventDepot);
Instantiator = new Instantiator(GetTree());
}
public void OnResolved()
{
GameBinding = GameLogic.Bind();
GameBinding
.Handle((in GameLogic.Output.StartGame _) => { })
.Handle((in GameLogic.Output.SetPauseMode output) => { CallDeferred(nameof(SetPauseMode), output.IsPaused); })
.Handle((in GameLogic.Output.SetInventoryMode _) => { InventoryMenu.PopulateItems(_.Inventory); InventoryMenu.Show(); })
.Handle((in GameLogic.Output.HideInventory _) => { InventoryMenu.Hide(); InventoryMenu.ClearItems(); })
.Handle((in GameLogic.Output.ShowMiniMap _) => { MiniMap.Show(); })
.Handle((in GameLogic.Output.HideMiniMap _) => { MiniMap.Hide(); })
.Handle((in GameLogic.Output.GameOver _) => { AppRepo.OnGameOver(); });
.Handle((in GameLogic.Output.StartGame _) =>
{
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(); });
GameLogic.Start();
GameLogic.Input(new GameLogic.Input.Initialize());
this.Provide();
PauseMenu.TransitionCompleted += OnPauseMenuTransitioned;
PauseMenu.UnpauseButtonPressed += PauseMenu_UnpauseButtonPressed;
GameEventDepot.TeleportEntered += Map_TeleportReached;
Map.DungeonFinishedGenerating += Map_DungeonFinishedGenerating;
InGameUI.InventoryMenu.ClosedMenu += InventoryMenu_CloseInventory;
InGameUI.MinimapButtonReleased += Player_MinimapButtonReleased;
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
GameRepo.PlayerData.Inventory.InventoryAtCapacity += PlayerInventory_InventoryAtCapacity;
GameRepo.PlayerData.Inventory.RaiseStatRequest += Inventory_RaiseStatRequest;
FloorClearMenu.GoToNextFloor += FloorClearMenu_GoToNextFloor;
FloorClearMenu.ReturnToHubWorld += ReturnToHubWorld;
FloorClearMenu.TransitionCompleted += FloorClearMenu_TransitionCompleted;
Player.InventoryButtonPressed += Player_InventoryButtonPressed;
Player.MinimapButtonHeld += Player_MinimapButtonHeld;
Player.PauseButtonPressed += Player_PauseButtonPressed;
GameRepo.PlayerData.Inventory.EquippedItem += Inventory_EquippedItem;
GameEventDepot.EnemyDefeated += OnEnemyDefeated;
GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
}
public override void _Process(double delta)
private void OnEnemyDefeated(Vector3 vector, EnemyStatResource resource)
{
if (Input.IsActionJustPressed(GameInputs.Inventory))
{
GD.Print("Inventory button pressed");
GameLogic.Input(new GameLogic.Input.InventoryMenuButtonPressed());
}
var restorativeScene = GD.Load<PackedScene>("res://src/items/restorative/Restorative.tscn");
var restorative = restorativeScene.Instantiate<Restorative>();
AddChild(restorative);
restorative.GlobalPosition = vector;
}
if (Input.IsActionJustPressed(GameInputs.MiniMap))
{
GD.Print("MiniMap button pressed");
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
}
private void Inventory_EquippedItem()
{
GameEventDepot.OnEquippedItem();
}
if (Input.IsActionJustReleased(GameInputs.MiniMap))
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 Player_MinimapButtonHeld()
{
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
}
private void Player_InventoryButtonPressed()
{
GameLogic.Input(new GameLogic.Input.OpenInventory());
}
private void FloorClearMenu_TransitionCompleted()
{
GameRepo.Resume();
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.BreaksOnChange))
GameRepo.PlayerData.Inventory.Unequip(GameRepo.PlayerData.Inventory.EquippedWeapon.Value);
}
private void FloorClearMenu_GoToNextFloor()
{
GameLogic.Input(new GameLogic.Input.GoToNextFloor());
}
private void ReturnToHubWorld()
{
// Implement a return to overworld state
// Don't carry over stats/equipment but we'll need to persist the overworld state
// Which may include rescued items and npc/questline state
GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
}
private void GameEventDepot_RestorativePickedUp(Restorative obj) => GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.CurrentVT.Value + obj.VTRestoreAmount);
private void Inventory_RaiseStatRequest(ConsumableItemStats consumableItemStats)
{
if (consumableItemStats.RaiseHPAmount > 0 && GameRepo.PlayerData.CurrentHP.Value.Equals(GameRepo.PlayerData.MaximumHP.Value))
RaiseHP(consumableItemStats.RaiseHPAmount);
else if (consumableItemStats.HealHPAmount > 0)
HealHP(consumableItemStats.HealHPAmount);
if (consumableItemStats.RaiseVTAmount > 0 && GameRepo.PlayerData.CurrentVT.Value.Equals(GameRepo.PlayerData.MaximumVT.Value))
RaiseVT(consumableItemStats.RaiseVTAmount);
else if (consumableItemStats.HealVTAmount > 0)
HealVT(consumableItemStats.HealVTAmount);
GameEventDepot.OnHealingItemConsumed(consumableItemStats);
}
private void RaiseHP(int amountToRaise)
{
if (GameRepo.PlayerData.CurrentHP.Value == GameRepo.PlayerData.MaximumHP.Value)
{
GD.Print("MiniMap button released");
GameLogic.Input(new GameLogic.Input.MiniMapButtonReleased());
GameRepo.PlayerData.SetMaximumHP(GameRepo.PlayerData.MaximumHP.Value + amountToRaise);
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.MaximumHP.Value);
EmitSignal(SignalName.StatRaisedAlert, $"{amountToRaise}MAXHP Up.");
}
}
@@ -85,5 +241,56 @@ public partial class Game : Node3D, IGame
GetTree().Paused = isPaused;
}
public void OnStart() => GameLogic.Input(new GameLogic.Input.Start());
private void HealHP(int amountToRaise)
{
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value + amountToRaise);
var raiseString = amountToRaise == 1000 ? "MAX" : $"{amountToRaise}";
EmitSignal(SignalName.StatRaisedAlert, $"{raiseString}HP Restored.");
}
private void RaiseVT(int amountToRaise)
{
if (GameRepo.PlayerData.CurrentVT.Value == GameRepo.PlayerData.MaximumVT.Value)
{
GameRepo.PlayerData.SetMaximumVT(GameRepo.PlayerData.MaximumVT.Value + amountToRaise);
GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.MaximumVT.Value);
EmitSignal(SignalName.StatRaisedAlert, $"{amountToRaise}MAXVT Up.");
}
}
private void HealVT(int amountToRaise)
{
GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.CurrentVT.Value + amountToRaise);
var raiseString = amountToRaise == 1000 ? "MAX" : $"{amountToRaise}";
EmitSignal(SignalName.StatRaisedAlert, $"{raiseString}VT Restored.");
}
private void PlayerInventory_InventoryAtCapacity(string rejectedItem)
{
InGameUI.PlayerInfoUI.DisplayInventoryFullMessage(rejectedItem);
}
private void OnInventoryAtCapacity(string rejectedItemName) => InGameUI.PlayerInfoUI.DisplayInventoryFullMessage(rejectedItemName);
private void InventoryMenu_CloseInventory() => GameLogic.Input(new GameLogic.Input.CloseInventory());
private void Map_DungeonFinishedGenerating()
{
var transform = Map.GetPlayerSpawnPosition();
GameRepo.SetPlayerGlobalPosition(transform.Origin);
GameRepo.SetPlayerGlobalTransform(transform);
GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
}
private void Map_TeleportReached()
{
GameLogic.Input(new GameLogic.Input.AskForTeleport());
}
private void OnPauseMenuTransitioned()
{
GameLogic.Input(new GameLogic.Input.PauseMenuTransitioned());
}
public void OnStart() => GameLogic.Input(new GameLogic.Input.StartGame());
}

View File

@@ -1,39 +1,128 @@
[gd_scene load_steps=7 format=3 uid="uid://33ek675mfb5n"]
[gd_scene load_steps=19 format=3 uid="uid://33ek675mfb5n"]
[ext_resource type="Script" path="res://src/game/Game.cs" id="1_ytcii"]
[ext_resource type="Shader" 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://dlj8qdg1c5048" path="res://src/inventory_menu/InventoryMenu.tscn" id="4_wk8gw"]
[ext_resource type="PackedScene" uid="uid://sv0suc4tjk8h" path="res://src/map/Overworld.tscn" id="5_cx613"]
[ext_resource type="PackedScene" uid="uid://bwbofurcvf3yh" path="res://src/minimap/Minimap.tscn" id="6_owlf4"]
[ext_resource type="Script" path="res://src/items/throwable/ThrowableItem.cs" id="5_2h2uw"]
[ext_resource type="PackedScene" uid="uid://b1muxus5qdbeu" path="res://src/ui/in_game_ui/InGameUI.tscn" id="5_lxtnp"]
[ext_resource type="Texture2D" uid="uid://dhfn51smm818x" path="res://src/items/throwable/textures/spell sign - luck.PNG" id="6_3qv3u"]
[ext_resource type="PackedScene" uid="uid://b16ejcwanod72" path="res://src/audio/InGameAudio.tscn" id="6_qc71l"]
[ext_resource type="Script" path="res://src/items/throwable/ThrowableItemStats.cs" id="7_1lafu"]
[ext_resource type="Script" path="res://src/game/DialogueController.cs" id="10_58pbt"]
[ext_resource type="Script" 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="NavigationMesh" id="NavigationMesh_xligp"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_e75a2"]
shader = ExtResource("2_6ifxs")
shader_parameter/change_color_depth = true
shader_parameter/target_color_depth = 7
shader_parameter/dithering = false
shader_parameter/scale_resolution = true
shader_parameter/target_resolution_scale = 4
shader_parameter/enable_recolor = false
[sub_resource type="Resource" id="Resource_qjdoa"]
script = ExtResource("7_1lafu")
Damage = 0
ThrowableItemTags = []
Name = "Test Item"
Description = ""
Texture = ExtResource("6_3qv3u")
SpawnRate = 0.5
[sub_resource type="BoxShape3D" id="BoxShape3D_qkug6"]
size = Vector3(0.371643, 0.289612, 0.286743)
[sub_resource type="BoxShape3D" id="BoxShape3D_1clvs"]
size = Vector3(0.778381, 0.929947, 0.731567)
[node name="Game" type="Node3D"]
process_mode = 3
script = ExtResource("1_ytcii")
[node name="Player" parent="." instance=ExtResource("3_kk6ly")]
[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
[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
transform = Transform3D(0.0871905, 0, -0.996192, 0, 1, 0, 0.996192, 0, 0.0871905, 0.81955, 0.00648284, -0.527107)
MoveSpeed = 8.0
Acceleration = 4.0
[node name="MiniMap" parent="." instance=ExtResource("6_owlf4")]
[node name="Player" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_kk6ly")]
unique_name_in_owner = true
process_mode = 1
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.74459, 1.22144)
[node name="Map" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_d8awv")]
unique_name_in_owner = true
process_mode = 1
[node name="ThrowableItem" type="Node3D" parent="SubViewportContainer/SubViewport/PauseContainer"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.52777, 3.48107, 3.48636)
script = ExtResource("5_2h2uw")
ThrowableItemInfo = SubResource("Resource_qjdoa")
[node name="Hitbox" type="RigidBody3D" parent="SubViewportContainer/SubViewport/PauseContainer/ThrowableItem"]
unique_name_in_owner = true
collision_layer = 0
collision_mask = 17
[node name="Sprite" type="Sprite3D" parent="SubViewportContainer/SubViewport/PauseContainer/ThrowableItem/Hitbox"]
unique_name_in_owner = true
pixel_size = 0.001
billboard = 1
shaded = true
double_sided = false
texture_filter = 0
render_priority = 100
texture = ExtResource("6_3qv3u")
[node name="CollisionShape3D" type="CollisionShape3D" parent="SubViewportContainer/SubViewport/PauseContainer/ThrowableItem/Hitbox"]
shape = SubResource("BoxShape3D_qkug6")
[node name="Pickup" type="Area3D" parent="SubViewportContainer/SubViewport/PauseContainer/ThrowableItem/Hitbox"]
unique_name_in_owner = true
collision_layer = 0
collision_mask = 4
[node name="CollisionShape3D" type="CollisionShape3D" parent="SubViewportContainer/SubViewport/PauseContainer/ThrowableItem/Hitbox/Pickup"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0600509, 0.26725, 0.180481)
shape = SubResource("BoxShape3D_1clvs")
[node name="InGameUI" parent="." instance=ExtResource("5_lxtnp")]
unique_name_in_owner = true
[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="InventoryMenu" parent="." instance=ExtResource("4_wk8gw")]
[node name="FloorClearMenu" parent="." instance=ExtResource("11_rya1n")]
unique_name_in_owner = true
visible = false
[node name="OmniLight3D" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 24.5244, 0)
layers = 3
omni_range = 163.618
omni_attenuation = -0.183
[node name="NavigationRegion" type="NavigationRegion3D" parent="."]
[node name="PauseMenu" parent="." instance=ExtResource("12_yev8k")]
unique_name_in_owner = true
navigation_mesh = SubResource("NavigationMesh_xligp")
[node name="Overworld" parent="." instance=ExtResource("5_cx613")]
visible = false
script = ExtResource("11_5ng8c")

View File

@@ -1,10 +1,12 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace GameJamDungeon
{
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
}
}

View File

@@ -0,0 +1,90 @@
using Godot;
using System;
namespace GameJamDungeon
{
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? EquippedItem;
public void OnEquippedItem();
event Action? UnequippedItem;
public void OnUnequippedItem();
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? EquippedItem;
public event Action? UnequippedItem;
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 OnEquippedItem() => EquippedItem?.Invoke();
public void OnUnequippedItem() => UnequippedItem?.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

@@ -4,17 +4,35 @@
{
public static class Input
{
public readonly record struct Start;
public readonly record struct StartGame;
public readonly record struct Initialize;
public readonly record struct InventoryMenuButtonPressed;
public readonly record struct GoToOverworld;
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

@@ -6,19 +6,39 @@ namespace GameJamDungeon
{
public static class Output
{
public readonly record struct StartGame();
public readonly record struct StartGame;
public readonly record struct SetInventoryMode(List<InventoryItem> Inventory);
public readonly record struct ShowPauseMenu;
public readonly record struct HideInventory();
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 ShowMiniMap;
public readonly record struct HideMiniMap();
public readonly record struct HideMiniMap;
public readonly record struct GameOver();
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;
}
}
}

View File

@@ -9,6 +9,6 @@ namespace GameJamDungeon
[LogicBlock(typeof(State), Diagram = true)]
public partial class GameLogic : LogicBlock<GameLogic.State>, IGameLogic
{
public override Transition GetInitialState() => To<State.Playing>();
public override Transition GetInitialState() => To<State.GameStarted>();
}
}

45
src/game/GameLogic.g.puml Normal file
View File

@@ -0,0 +1,45 @@
@startuml GameLogic
state "GameLogic State" as GameJamDungeon_GameLogic_State {
state "GameStarted" as GameJamDungeon_GameLogic_State_GameStarted
state "Playing" as GameJamDungeon_GameLogic_State_Playing {
state "AskForTeleport" as GameJamDungeon_GameLogic_State_AskForTeleport
state "FloorClearedDecisionState" as GameJamDungeon_GameLogic_State_FloorClearedDecisionState
state "InventoryOpened" as GameJamDungeon_GameLogic_State_InventoryOpened
state "MinimapOpen" as GameJamDungeon_GameLogic_State_MinimapOpen
state "Paused" as GameJamDungeon_GameLogic_State_Paused
state "Resuming" as GameJamDungeon_GameLogic_State_Resuming
}
state "Quit" as GameJamDungeon_GameLogic_State_Quit
}
GameJamDungeon_GameLogic_State_AskForTeleport --> GameJamDungeon_GameLogic_State_FloorClearedDecisionState : FloorExitReached
GameJamDungeon_GameLogic_State_AskForTeleport --> GameJamDungeon_GameLogic_State_Playing : HideAskForTeleport
GameJamDungeon_GameLogic_State_FloorClearedDecisionState --> GameJamDungeon_GameLogic_State_FloorClearedDecisionState : GoToNextFloor
GameJamDungeon_GameLogic_State_FloorClearedDecisionState --> GameJamDungeon_GameLogic_State_Playing : HideFloorClearMenu
GameJamDungeon_GameLogic_State_GameStarted --> GameJamDungeon_GameLogic_State_Playing : Initialize
GameJamDungeon_GameLogic_State_InventoryOpened --> GameJamDungeon_GameLogic_State_Playing : CloseInventory
GameJamDungeon_GameLogic_State_MinimapOpen --> GameJamDungeon_GameLogic_State_Playing : MiniMapButtonReleased
GameJamDungeon_GameLogic_State_Paused --> GameJamDungeon_GameLogic_State_Resuming : UnpauseGame
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_AskForTeleport : AskForTeleport
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_InventoryOpened : OpenInventory
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_MinimapOpen : MiniMapButtonPressed
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_Paused : PauseGame
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_Playing : GoToOverworld
GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_Quit : GameOver
GameJamDungeon_GameLogic_State_Resuming --> GameJamDungeon_GameLogic_State_Playing : PauseMenuTransitioned
GameJamDungeon_GameLogic_State : OnIsPaused() → SetPauseMode
GameJamDungeon_GameLogic_State_FloorClearedDecisionState : OnGoToNextFloor → LoadNextFloor
GameJamDungeon_GameLogic_State_GameStarted : OnInitialize → StartGame
GameJamDungeon_GameLogic_State_InventoryOpened : OnEnter → OpenInventory
GameJamDungeon_GameLogic_State_InventoryOpened : OnExit → HideInventory
GameJamDungeon_GameLogic_State_MinimapOpen : OnEnter → ShowMiniMap
GameJamDungeon_GameLogic_State_MinimapOpen : OnExit → HideMiniMap
GameJamDungeon_GameLogic_State_Paused : OnEnter → ShowPauseMenu
GameJamDungeon_GameLogic_State_Paused : OnExit → ExitPauseMenu
GameJamDungeon_GameLogic_State_Playing : OnGoToOverworld → GoToOverworld
GameJamDungeon_GameLogic_State_Quit : OnEnter → ShowLostScreen
GameJamDungeon_GameLogic_State_Resuming : OnExit → HidePauseMenu
[*] --> GameJamDungeon_GameLogic_State_GameStarted
@enduml

106
src/game/GameRepo.cs Normal file
View File

@@ -0,0 +1,106 @@
using Chickensoft.Collections;
using Godot;
using System;
namespace GameJamDungeon;
public interface IGameRepo : IDisposable
{
void Pause();
void Resume();
IAutoProp<bool> IsPaused { get; }
IAutoProp<Vector3> PlayerGlobalPosition { get; }
IAutoProp<Transform3D> PlayerGlobalTransform { get; }
PlayerData PlayerData { get; }
public void SetPlayerData(PlayerData playerData);
void SetPlayerGlobalPosition(Vector3 playerGlobalPosition);
void SetPlayerGlobalTransform(Transform3D playerGlobalTransform);
public int MaxItemSize { get; }
public int CurrentFloor { get; set; }
}
public class GameRepo : IGameRepo
{
public event Action? Ended;
public IAutoProp<Vector3> PlayerGlobalPosition => _playerGlobalPosition;
private readonly AutoProp<Vector3> _playerGlobalPosition;
public IAutoProp<Transform3D> PlayerGlobalTransform => _playerGlobalTransform;
private readonly AutoProp<Transform3D> _playerGlobalTransform;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
public PlayerData PlayerData => _playerData;
private PlayerData _playerData;
public int MaxItemSize => 20;
private bool _disposedValue;
public int CurrentFloor { get; set; } = 0;
public GameRepo()
{
_isPaused = new AutoProp<bool>(false);
_playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero);
_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 SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition);
public void SetPlayerGlobalTransform(Transform3D playerGlobalTransform) => _playerGlobalTransform.OnNext(playerGlobalTransform);
public void SetPlayerData(PlayerData playerData) => _playerData = playerData;
public void OnGameEnded()
{
Pause();
Ended?.Invoke();
}
protected void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_playerGlobalPosition.OnCompleted();
_playerGlobalPosition.Dispose();
_isPaused.OnCompleted();
_isPaused.Dispose();
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

View File

@@ -1,146 +0,0 @@
using Chickensoft.Collections;
using Godot;
using System;
using System.Collections.Generic;
namespace GameJamDungeon;
public interface IGameRepo : IDisposable
{
event Action? Ended;
AutoProp<List<InventoryItem>> InventoryItems { get; }
IAutoProp<bool> IsInventoryScreenOpened { get; }
IAutoProp<bool> IsPaused { get; }
void Pause();
void Resume();
IAutoProp<Vector3> PlayerGlobalPosition { get; }
void SetPlayerGlobalPosition(Vector3 playerGlobalPosition);
public void OnWeaponEquipped(Weapon equippedItem);
public void OnArmorEquipped(Armor equippedItem);
public void OnAccessoryEquipped(Accessory equippedItem);
public Weapon EquippedWeapon { get; }
public Armor EquippedArmor { get; }
public Accessory EquippedAccessory { get; }
public AutoProp<int> HPBarValue { get; }
public AutoProp<int> VTBarValue { get; }
}
public class GameRepo : IGameRepo
{
public event Action? Ended;
private readonly AutoProp<List<InventoryItem>> _inventoryItems;
private readonly AutoProp<bool> _isInventoryScreenOpened;
public AutoProp<List<InventoryItem>> InventoryItems => _inventoryItems;
public IAutoProp<bool> IsInventoryScreenOpened => _isInventoryScreenOpened;
public IAutoProp<Vector3> PlayerGlobalPosition => _playerGlobalPosition;
private readonly AutoProp<Vector3> _playerGlobalPosition;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
private Weapon _equippedWeapon;
public Weapon EquippedWeapon => _equippedWeapon;
private Armor _equippedArmor;
public Armor EquippedArmor => _equippedArmor;
private Accessory _equippedAccessory;
public Accessory EquippedAccessory => _equippedAccessory;
public AutoProp<int> HPBarValue { get; }
public AutoProp<int> VTBarValue { get; }
private bool _disposedValue;
public GameRepo()
{
_inventoryItems = new AutoProp<List<InventoryItem>>([]);
_isInventoryScreenOpened = new AutoProp<bool>(false);
_isPaused = new AutoProp<bool>(false);
_playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero);
_equippedWeapon = new Weapon();
HPBarValue = new AutoProp<int>(0);
VTBarValue = new AutoProp<int>(0);
}
public void Pause()
{
_isPaused.OnNext(true);
GD.Print("Paused");
}
public void Resume()
{
_isPaused.OnNext(false);
GD.Print("Resume");
}
public void SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition);
public void OnWeaponEquipped(Weapon equippedItem)
{
_equippedWeapon = equippedItem;
}
public void OnArmorEquipped(Armor equippedItem)
{
_equippedArmor = equippedItem;
}
public void OnAccessoryEquipped(Accessory equippedItem)
{
_equippedAccessory = equippedItem;
}
public void OnGameEnded()
{
Pause();
Ended?.Invoke();
}
protected void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_playerGlobalPosition.OnCompleted();
_playerGlobalPosition.Dispose();
_inventoryItems.OnCompleted();
_inventoryItems.Dispose();
_isInventoryScreenOpened.OnCompleted();
_isInventoryScreenOpened.Dispose();
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

View File

@@ -0,0 +1,28 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
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,32 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record FloorClearedDecisionState : Playing, IGet<Input.GoToNextFloor>, 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>();
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record GameStarted : State, IGet<Input.Initialize>
{
public GameStarted()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); });
this.OnExit(() => { Get<IGameRepo>().Resume(); });
}
public Transition On(in Input.Initialize input)
{
Output(new Output.StartGame());
return To<Playing>();
}
}
}
}
}

View File

@@ -8,14 +8,18 @@ namespace GameJamDungeon
public partial record State
{
[Meta]
public partial record InventoryOpened : State, IGet<Input.InventoryMenuButtonPressed>
public partial record InventoryOpened : Playing, IGet<Input.CloseInventory>
{
public InventoryOpened()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.SetInventoryMode(Get<IGameRepo>().InventoryItems.Value)); });
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.OpenInventory()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideInventory()); });
}
public Transition On(in Input.InventoryMenuButtonPressed input) => To<Playing>();
public Transition On(in Input.CloseInventory input)
{
return To<Playing>();
}
}
}
}

View File

@@ -8,7 +8,7 @@ namespace GameJamDungeon
public partial record State
{
[Meta]
public partial record MinimapOpen : State, IGet<Input.MiniMapButtonReleased>
public partial record MinimapOpen : Playing, IGet<Input.MiniMapButtonReleased>
{
public MinimapOpen()
{

View File

@@ -0,0 +1,27 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
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,43 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
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()
{
}
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

@@ -12,7 +12,7 @@ namespace GameJamDungeon
{
public Quit()
{
this.OnEnter(() => Output(new Output.GameOver()));
this.OnEnter(() => Output(new Output.ShowLostScreen()));
}
}
}

View File

@@ -0,0 +1,23 @@
namespace GameJamDungeon;
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

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

View File

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

View File

@@ -1,34 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Playing : State, IGet<Input.InventoryMenuButtonPressed>, IGet<Input.MiniMapButtonPressed>, IGet<Input.GameOver>
{
public Playing()
{
this.OnEnter(() => { Output(new Output.StartGame()); });
OnAttach(() => Get<IGameRepo>().Ended += OnEnded);
OnDetach(() => Get<IGameRepo>().Ended -= OnEnded);
}
public void OnEnded() => Input(new Input.GameOver());
public Transition On(in Input.InventoryMenuButtonPressed input) => To<InventoryOpened>();
public Transition On(in Input.MiniMapButtonPressed input) => To<MinimapOpen>();
public Transition On(in Input.GameOver input)
{
return To<Quit>();
}
}
}
}
}