Implement BGM and SFX event system

This commit is contained in:
2024-09-14 16:01:55 -07:00
parent 0a71339bbe
commit cd8806a9fe
38 changed files with 718 additions and 116 deletions

View File

@@ -19,7 +19,7 @@ stretch = true
unique_name_in_owner = true
transparent_bg = true
handle_input_locally = false
audio_listener_enable_3d = true
audio_listener_enable_2d = true
size = Vector2i(1920, 1080)
render_target_update_mode = 4

View File

@@ -10,12 +10,121 @@ public partial class InGameAudio : Node
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
#region BGM Nodes
[Node] public IDimmableAudioStreamPlayer MenuBGM { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer OverworldBGM { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer MenuBgm { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer OverworldBgm { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer DungeonThemeABgm { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer DungeonThemeBBgm { get; set; } = default!;
[Node] public IDimmableAudioStreamPlayer DungeonThemeCBgm { get; set; } = default!;
#endregion
#region SFX Nodes
[Node] public IAudioStreamPlayer PlayerAttackSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer MenuScrollSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer EquipSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer MenuBackSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer InventorySortedSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer HealingItemSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer TeleportSFX { get; set; } = default!;
#endregion
public IInGameAudioLogic InGameAudioLogic { get; set; } = default!;
public InGameAudioLogic.IBinding InGameAudioBinding { get; set; } = default!;
public void Setup()
{
InGameAudioLogic = new InGameAudioLogic();
}
public void OnResolved()
{
InGameAudioLogic.Set(AppRepo);
InGameAudioLogic.Set(GameRepo);
InGameAudioLogic.Set(GameEventDepot);
InGameAudioBinding = InGameAudioLogic.Bind();
InGameAudioBinding
.Handle((in InGameAudioLogic.Output.PlayOverworldMusic _) => StartOverworldMusic())
.Handle((in InGameAudioLogic.Output.PlayDungeonThemeAMusic _) => StartDungeonThemeA())
.Handle((in InGameAudioLogic.Output.PlayMenuScrollSound _) => PlayMenuScrollSound())
.Handle((in InGameAudioLogic.Output.PlayEquipSound _) => PlayEquipSound())
.Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound())
.Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound())
.Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound())
.Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound());
InGameAudioLogic.Start();
}
public void OnExitTree()
{
InGameAudioLogic.Stop();
InGameAudioBinding.Dispose();
}
private void StartOverworldMusic()
{
OverworldBgm.Stop();
OverworldBgm.FadeIn();
}
private void StartDungeonThemeA()
{
OverworldBgm.FadeOut();
DungeonThemeABgm.Stop();
DungeonThemeABgm.FadeIn();
}
private void PlayMenuScrollSound()
{
MenuScrollSFX.Stop();
MenuScrollSFX.Play();
}
private void PlayEquipSound()
{
EquipSFX.Stop();
EquipSFX.Play();
}
private void PlayMenuBackSound()
{
MenuBackSFX.Stop();
MenuBackSFX.Play();
}
private void PlayInventorySortedSound()
{
InventorySortedSFX.Stop();
InventorySortedSFX.Play();
}
private void PlayHealingItemSound()
{
HealingItemSFX.Stop();
HealingItemSFX.Play();
}
private void PlayTeleportSound()
{
TeleportSFX.Stop();
TeleportSFX.Play();
}
}

View File

@@ -1,19 +1,93 @@
[gd_scene load_steps=3 format=3 uid="uid://b16ejcwanod72"]
[gd_scene load_steps=13 format=3 uid="uid://b16ejcwanod72"]
[ext_resource type="Script" path="res://src/audio/InGameAudio.cs" id="1_gpmcr"]
[ext_resource type="AudioStream" uid="uid://dfu0fksb6slhx" path="res://src/audio/music/droney.mp3" id="2_8hfyr"]
[ext_resource type="Script" path="res://src/audio/DimmableAudioStreamPlayer.cs" id="2_857rw"]
[ext_resource type="AudioStream" uid="uid://d2jrktp06xsba" path="res://src/audio/music/crossing-the-gate.mp3" id="3_wbmd6"]
[ext_resource type="AudioStream" uid="uid://dn2e2hqujlia1" path="res://src/audio/music/tar-winds.mp3" id="4_surnl"]
[ext_resource type="AudioStream" uid="uid://t3g04u722f2k" path="res://src/audio/music/useless immune system-1.mp3" id="6_agr3r"]
[ext_resource type="AudioStream" uid="uid://dor0in0x2fg48" path="res://src/audio/sfx/TempFFVII/menu-move.ogg" id="7_777nl"]
[ext_resource type="AudioStream" uid="uid://r1tryiit38i8" path="res://src/audio/sfx/TempFFVII/menu-back.ogg" id="8_1xcgo"]
[ext_resource type="AudioStream" uid="uid://bjj61s8q2gwb8" path="res://src/audio/sfx/TempFFVII/junction.ogg" id="8_kwybb"]
[ext_resource type="AudioStream" uid="uid://myx4s8lmarc2" path="res://src/audio/sfx/TempFFVII/something-earned.ogg" id="10_3lcw5"]
[ext_resource type="AudioStream" uid="uid://dci08kmwsu6k1" path="res://src/audio/sfx/TempFFVII/teleport.ogg" id="11_offhc"]
[ext_resource type="AudioStream" uid="uid://d3sn7c614uj2n" path="res://src/audio/sfx/TempFFVII/sort.ogg" id="12_wprjr"]
[node name="InGameAudio" type="Node"]
process_mode = 3
script = ExtResource("1_gpmcr")
[node name="MenuBGM" type="AudioStreamPlayer" parent="."]
[node name="MenuBgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("2_8hfyr")
parameters/looping = true
script = ExtResource("2_857rw")
[node name="OverworldBGM" type="AudioStreamPlayer" parent="."]
[node name="OverworldBgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("3_wbmd6")
volume_db = -15.0
parameters/looping = true
script = ExtResource("2_857rw")
[node name="DungeonThemeABgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("4_surnl")
volume_db = -15.0
parameters/looping = true
script = ExtResource("2_857rw")
[node name="DungeonThemeBBgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("6_agr3r")
volume_db = -15.0
parameters/looping = true
script = ExtResource("2_857rw")
[node name="DungeonThemeCBgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
volume_db = -15.0
script = ExtResource("2_857rw")
[node name="Title_Bgm" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
volume_db = -15.0
script = ExtResource("2_857rw")
[node name="PlayerAttackSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
[node name="MenuScrollSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("7_777nl")
volume_db = -10.0
[node name="MenuBackSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("8_1xcgo")
volume_db = -10.0
[node name="EquipSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("8_kwybb")
volume_db = -10.0
[node name="HealSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("10_3lcw5")
volume_db = -10.0
[node name="TeleportSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("11_offhc")
volume_db = -18.0
[node name="InventorySortedSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("12_wprjr")
volume_db = -15.0
[node name="HealingItemSFX" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("10_3lcw5")
volume_db = -15.0

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://t3g04u722f2k"
path="res://.godot/imported/useless immune system-1.mp3-e40a7d02f05bda566c0eac2b33f080a0.mp3str"
[deps]
source_file="res://src/audio/music/useless immune system-1.mp3"
dest_files=["res://.godot/imported/useless immune system-1.mp3-e40a7d02f05bda566c0eac2b33f080a0.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,23 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://t28qhjuibv3f"
valid=false
[deps]
source_file="res://src/audio/sfx/TempFFVII/Equip.wav"
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bjj61s8q2gwb8"
path="res://.godot/imported/junction.ogg-f4350086a08e048d3008edcdc25abf96.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/junction.ogg"
dest_files=["res://.godot/imported/junction.ogg-f4350086a08e048d3008edcdc25abf96.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://r1tryiit38i8"
path="res://.godot/imported/menu-back.ogg-3ec385c1a9cfaaa1be4ba85197708f0c.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu-back.ogg"
dest_files=["res://.godot/imported/menu-back.ogg-3ec385c1a9cfaaa1be4ba85197708f0c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dor0in0x2fg48"
path="res://.godot/imported/menu-move.ogg-d71b0989e00dd1d4488a72c7dde3d41d.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu-move.ogg"
dest_files=["res://.godot/imported/menu-move.ogg-d71b0989e00dd1d4488a72c7dde3d41d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://tce7m18vkgao"
path="res://.godot/imported/menu.wav-3931712b8a8483f269018c4a880368b2.sample"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu.wav"
dest_files=["res://.godot/imported/menu.wav-3931712b8a8483f269018c4a880368b2.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://myx4s8lmarc2"
path="res://.godot/imported/something-earned.ogg-150627e4deb45db30e8dd2f98ddcc5a8.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/something-earned.ogg"
dest_files=["res://.godot/imported/something-earned.ogg-150627e4deb45db30e8dd2f98ddcc5a8.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://d3sn7c614uj2n"
path="res://.godot/imported/sort.ogg-cb2a2c4769c8e6574221a3c313e75bcf.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/sort.ogg"
dest_files=["res://.godot/imported/sort.ogg-cb2a2c4769c8e6574221a3c313e75bcf.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dci08kmwsu6k1"
path="res://.godot/imported/teleport.ogg-9024f7b675b201a391dee183da020b1d.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/teleport.ogg"
dest_files=["res://.godot/imported/teleport.ogg-9024f7b675b201a391dee183da020b1d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,34 @@
namespace GameJamDungeon
{
public partial class InGameAudioLogic
{
public static class Output
{
#region BGM
public readonly record struct PlayOverworldMusic;
public readonly record struct PlayDungeonThemeAMusic;
#endregion
#region SFX
public readonly record struct PlayPlayerAttackSound;
public readonly record struct PlayMenuScrollSound;
public readonly record struct PlayEquipSound;
public readonly record struct PlayInventorySortedSound;
public readonly record struct PlayMenuBackSound;
public readonly record struct PlayHealingItemSound;
public readonly record struct PlayTeleportSound;
#endregion
public readonly record struct PlayGameMusic;
public readonly record struct StopGameMusic;
}
}
}

View File

@@ -0,0 +1,55 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using System;
namespace GameJamDungeon;
public partial class InGameAudioLogic
{
[Meta]
public partial record State : StateLogic<State>
{
public State()
{
OnAttach(() =>
{
var gameEventDepot = Get<IGameEventDepot>();
gameEventDepot.OverworldEntered += OnOverworldEntered;
gameEventDepot.DungeonAThemeAreaEntered += OnDungeonAThemeEntered;
gameEventDepot.MenuScrolled += OnMenuScrolled;
gameEventDepot.MenuBackedOut += OnMenuBackedOut;
gameEventDepot.EquippedItem += OnEquippedItem;
gameEventDepot.InventorySorted += OnInventorySorted;
gameEventDepot.HealingItemConsumed += OnHealingItemConsumed;
gameEventDepot.TeleportEntered += OnTeleportEntered;
});
OnDetach(() =>
{
var gameEventDepot = Get<IGameEventDepot>();
gameEventDepot.OverworldEntered -= OnOverworldEntered;
gameEventDepot.DungeonAThemeAreaEntered -= OnDungeonAThemeEntered;
gameEventDepot.MenuScrolled -= OnMenuScrolled;
gameEventDepot.MenuBackedOut -= OnMenuBackedOut;
gameEventDepot.EquippedItem -= OnEquippedItem;
gameEventDepot.InventorySorted -= OnInventorySorted;
gameEventDepot.TeleportEntered -= OnTeleportEntered;
});
}
private void OnMenuBackedOut() => Output(new Output.PlayMenuBackSound());
private void OnHealingItemConsumed(ConsumableItemStats stats) => Output(new Output.PlayHealingItemSound());
private void OnInventorySorted() => Output(new Output.PlayInventorySortedSound());
private void OnEquippedItem() => Output(new Output.PlayEquipSound());
private void OnOverworldEntered() => Output(new Output.PlayOverworldMusic());
private void OnDungeonAThemeEntered() => Output(new Output.PlayDungeonThemeAMusic());
private void OnMenuScrolled() => Output(new Output.PlayMenuScrollSound());
private void OnTeleportEntered() => Output(new Output.PlayTeleportSound());
}
}

View File

@@ -0,0 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon;
public interface IInGameAudioLogic : ILogicBlock<InGameAudioLogic.State>;
[Meta]
[LogicBlock(typeof(State))]
public partial class InGameAudioLogic :
LogicBlock<InGameAudioLogic.State>, IInGameAudioLogic
{
public override Transition GetInitialState() => To<State>();
}

View File

@@ -4,11 +4,9 @@ namespace GameJamDungeon;
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using DialogueManagerRuntime;
using Godot;
using System;
public interface IGame : IProvide<IGameRepo>, IProvide<IGame>, INode3D
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, INode3D
{
event Game.StatRaisedAlertEventHandler StatRaisedAlert;
}
@@ -22,10 +20,14 @@ public partial class Game : Node3D, IGame
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!;
@@ -47,14 +49,18 @@ public partial class Game : Node3D, IGame
[Node] public DeathMenu DeathMenu { get; set; } = default!;
[Node] public IPauseMenu PauseMenu { get; set; } = default!;
[Node] public InGameAudio InGameAudio { get; set; } = default!;
#endregion
public void Setup()
{
GameRepo = new GameRepo();
GameLogic = new GameLogic();
GameEventDepot = new GameEventDepot();
GameLogic.Set(GameRepo);
GameLogic.Set(AppRepo);
GameLogic.Set(GameEventDepot);
Instantiator = new Instantiator(GetTree());
}
@@ -66,6 +72,10 @@ public partial class Game : Node3D, IGame
{
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 _) =>
{
@@ -93,12 +103,13 @@ public partial class Game : Node3D, IGame
GameLogic.Start();
GameLogic.Input(new GameLogic.Input.Initialize());
InGameAudio.OverworldBgm.FadeIn();
this.Provide();
PauseMenu.TransitionCompleted += OnPauseMenuTransitioned;
PauseMenu.UnpauseButtonPressed += PauseMenu_UnpauseButtonPressed;
Map.TeleportReached += Map_TeleportReached;
GameEventDepot.TeleportEntered += Map_TeleportReached;
Map.DungeonFinishedGenerating += Map_DungeonFinishedGenerating;
InGameUI.InventoryMenu.ClosedMenu += InventoryMenu_CloseInventory;
InGameUI.MinimapButtonReleased += Player_MinimapButtonReleased;
@@ -115,6 +126,13 @@ public partial class Game : Node3D, IGame
Player.InventoryButtonPressed += Player_InventoryButtonPressed;
Player.MinimapButtonHeld += Player_MinimapButtonHeld;
Player.PauseButtonPressed += Player_PauseButtonPressed;
GameRepo.PlayerData.Inventory.EquippedItem += Inventory_EquippedItem;
}
private void Inventory_EquippedItem()
{
GameEventDepot.OnEquippedItem();
}
private void UseTeleportPrompt_CloseTeleportPrompt()
@@ -125,6 +143,7 @@ public partial class Game : Node3D, IGame
private void UseTeleportPrompt_TeleportToNextFloor()
{
GameLogic.Input(new GameLogic.Input.FloorExitReached());
GameEventDepot.OnDungeonAThemeAreaEntered();
}
private void PauseMenu_UnpauseButtonPressed()
@@ -170,10 +189,6 @@ public partial class Game : Node3D, IGame
GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
}
public void GoToNextFloor()
{
GameLogic.Input(new GameLogic.Input.FloorExitReached());
}
private void Inventory_RaiseStatRequest(ConsumableItemStats consumableItemStats)
{
@@ -186,6 +201,8 @@ public partial class Game : Node3D, IGame
RaiseVT(consumableItemStats.RaiseVTAmount);
else if (consumableItemStats.HealVTAmount > 0)
HealVT(consumableItemStats.HealVTAmount);
GameEventDepot.OnHealingItemConsumed(consumableItemStats);
}
private void RaiseHP(int amountToRaise)

View File

@@ -34,6 +34,7 @@ script = ExtResource("4_f5pye")
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

View File

@@ -0,0 +1,77 @@
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);
}
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 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 Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@@ -8,6 +8,8 @@
public readonly record struct Initialize;
public readonly record struct GoToOverworld;
public readonly record struct OpenInventory;
public readonly record struct CloseInventory;

View File

@@ -37,6 +37,8 @@ namespace GameJamDungeon
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>();
}
}

View File

@@ -24,6 +24,7 @@ GameJamDungeon_GameLogic_State_Playing --> GameJamDungeon_GameLogic_State_AskFor
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
@@ -36,8 +37,9 @@ 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_Playing
[*] --> GameJamDungeon_GameLogic_State_GameStarted
@enduml

View File

@@ -23,10 +23,6 @@ public interface IGameRepo : IDisposable
public int MaxItemSize { get; }
public int CurrentFloor { get; set; }
public void OnGameEnded();
event Action? Ended;
}
public class GameRepo : IGameRepo

View File

@@ -13,12 +13,11 @@ namespace GameJamDungeon
IGet<Input.MiniMapButtonPressed>,
IGet<Input.GameOver>,
IGet<Input.AskForTeleport>,
IGet<Input.PauseGame>
IGet<Input.PauseGame>,
IGet<Input.GoToOverworld>
{
public Playing()
{
this.OnEnter(() => Get<IGameRepo>().Ended += OnEnded);
this.OnExit(() => Get<IGameRepo>().Ended -= OnEnded);
}
public void OnEnded() => Input(new Input.GameOver());
@@ -32,6 +31,12 @@ namespace GameJamDungeon
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

@@ -24,6 +24,9 @@ public partial class InventoryMenu : Control, IInventoryMenu
[Dependency]
public IGame Game => this.DependOn<IGame>();
[Dependency]
public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Signal]
public delegate void ClosedMenuEventHandler();
@@ -100,7 +103,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
await HideUserActionPrompt();
await ShowInventoryInfo();
ItemEffectLabel.Text = statRaisedAlert;
await ToSignal(GetTree().CreateTimer(1.5f), "timeout");
await ToSignal(GetTree().CreateTimer(1f), "timeout");
await RedrawInventory();
SetProcessInput(true);
}
@@ -151,6 +154,8 @@ public partial class InventoryMenu : Control, IInventoryMenu
await ClearItems();
PopulateInventory();
PopulatePlayerInfo();
await HideUserActionPrompt();
await ShowInventoryInfo();
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
@@ -164,6 +169,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{
HideUserActionPrompt();
ShowInventoryInfo();
GameEventDepot.OnMenuBackedOut();
}
else
{
@@ -182,16 +188,29 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (@event.IsActionPressed(GameInputs.UiDown))
{
SetToUnselectedStyle(ItemSlots.ElementAt(_currentIndex));
_currentIndex = new[] { _currentIndex + 1, _itemsPerPage - 1, ItemSlots.Length - 1 }.Min();
SetToSelectedStyle(ItemSlots.ElementAt(_currentIndex));
var oldIndex = _currentIndex;
var newIndex = new[] { _currentIndex + 1, _itemsPerPage - 1, ItemSlots.Length - 1 }.Min();
if (oldIndex == newIndex)
return;
SetToUnselectedStyle(ItemSlots.ElementAt(oldIndex));
SetToSelectedStyle(ItemSlots.ElementAt(newIndex));
GameEventDepot.OnMenuScrolled();
_currentIndex = newIndex;
}
if (@event.IsActionPressed(GameInputs.UiUp))
{
SetToUnselectedStyle(ItemSlots.ElementAt(_currentIndex));
_currentIndex = new[] { _currentIndex - 1, 0 }.Max();
SetToSelectedStyle(ItemSlots.ElementAt(_currentIndex));
var oldIndex = _currentIndex;
var newIndex = new[] { _currentIndex - 1, 0 }.Max();
if (oldIndex == newIndex)
return;
SetToUnselectedStyle(ItemSlots.ElementAt(oldIndex));
SetToSelectedStyle(ItemSlots.ElementAt(newIndex));
GameEventDepot.OnMenuScrolled();
_currentIndex = newIndex;
}
if (@event.IsActionPressed(GameInputs.UiAccept))
@@ -202,6 +221,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (@event.IsActionPressed(GameInputs.InventorySort))
{
inventory.Sort();
GameEventDepot.OnInventorySorted();
RedrawInventory();
}
}
@@ -283,9 +303,10 @@ public partial class InventoryMenu : Control, IInventoryMenu
_currentIndex = 0;
_currentPageNumber = pageToChangeTo;
await RedrawInventory();
GameEventDepot.OnMenuScrolled();
}
private void PopulateInventory()
private async void PopulateInventory()
{
var inventory = GameRepo.PlayerData.Inventory;
var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Items.Count, _itemsPerPage) : Mathf.Min(inventory.Items.Count - _itemsPerPage, _itemsPerPage);
@@ -362,7 +383,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
SetProcessInput(false);
await HideUserActionPrompt();
await ShowInventoryInfo();
await ToSignal(GetTree().CreateTimer(1.5f), "timeout");
await ToSignal(GetTree().CreateTimer(1f), "timeout");
await RedrawInventory();
SetProcessInput(true);
}
@@ -376,6 +397,11 @@ public partial class InventoryMenu : Control, IInventoryMenu
else if (currentItem is ConsumableItem consumable)
{
GameRepo.PlayerData.Inventory.Use(consumable);
GameEventDepot.OnHealingItemConsumed(consumable.ConsumableItemInfo);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
}
}

View File

@@ -189,10 +189,10 @@ layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_bottom = 194.0
offset_bottom = -3.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_top = 112
theme_override_constants/margin_top = 100
[node name="HBoxContainer" type="HBoxContainer" parent="InventoryInfo"]
layout_mode = 2
@@ -507,31 +507,36 @@ alignment = 0
layout_mode = 2
theme_override_constants/separation = 20
[node name="CenterContainer" type="CenterContainer" parent="InventoryInfo/HBoxContainer/ItemInfo"]
custom_minimum_size = Vector2(800, 0)
[node name="ItemTitleContainer" type="MarginContainer" parent="InventoryInfo/HBoxContainer/ItemInfo"]
custom_minimum_size = Vector2(800, 125)
layout_mode = 2
size_flags_horizontal = 4
[node name="BackArrow" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/CenterContainer"]
[node name="BackArrow" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemTitleContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(800, 0)
visible = false
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "◄"
label_settings = SubResource("LabelSettings_x4aj3")
[node name="ItemsTitle" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/CenterContainer"]
[node name="ItemsTitle" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemTitleContainer"]
custom_minimum_size = Vector2(450, 0)
layout_mode = 2
size_flags_horizontal = 4
size_flags_horizontal = 3
size_flags_vertical = 1
text = " ITEMS"
label_settings = SubResource("LabelSettings_31kc7")
horizontal_alignment = 1
vertical_alignment = 1
[node name="ForwardArrow" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/CenterContainer"]
[node name="ForwardArrow" type="Label" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemTitleContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(800, 100)
visible = false
custom_minimum_size = Vector2(800, 0)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "►"
label_settings = SubResource("LabelSettings_x4aj3")
horizontal_alignment = 2

View File

@@ -40,6 +40,8 @@ public interface IInventory : INode
event Inventory.AccessoryUnequippedEventHandler AccessoryUnequipped;
event Inventory.RaiseStatRequestEventHandler RaiseStatRequest;
event Inventory.EquippedItemEventHandler EquippedItem;
}
public partial class Inventory : Node, IInventory
@@ -53,6 +55,8 @@ public partial class Inventory : Node, IInventory
public delegate void AccessoryUnequippedEventHandler(AccessoryStats unequippedAccessory);
[Signal]
public delegate void RaiseStatRequestEventHandler(ConsumableItemStats consumableItemStats);
[Signal]
public delegate void EquippedItemEventHandler();
public Inventory()
{
@@ -94,6 +98,8 @@ public partial class Inventory : Node, IInventory
_equippedAccessory.OnNext(accessory);
else
throw new NotImplementedException("Item type is not supported.");
EmitSignal(SignalName.EquippedItem);
}
public void Unequip(IEquipable equipable)
@@ -157,7 +163,7 @@ public partial class Inventory : Node, IInventory
var accessories = listToSort.Where(x => x is Accessory).OrderBy(x => x as Accessory, new AccessoryComparer());
var consumables = listToSort.Where(x => x is ConsumableItem).OrderBy(x => x as ConsumableItem, new ConsumableComparer());
var throwables = listToSort.Where(x => x is ThrowableItem).OrderBy(x => x as ThrowableItem, new ThrowableComparer());
Items = [.. consumables, .. equippedItems, .. weapons, .. armor, .. accessories, .. throwables];
Items = [.. equippedItems, .. weapons, .. armor, .. accessories, .. consumables, .. throwables];
}
public class WeaponComparer : IComparer<Weapon>

View File

@@ -10,7 +10,6 @@ namespace GameJamDungeon;
public interface IMap : INode3D
{
event Map.TeleportReachedEventHandler TeleportReached;
event Map.DungeonFinishedGeneratingEventHandler DungeonFinishedGenerating;
public List<IDungeonFloor> Floors { get; }
@@ -28,12 +27,12 @@ public partial class Map : Node3D, IMap
[Node] public Area3D Teleport { get; set; } = default!;
[Signal]
public delegate void TeleportReachedEventHandler();
[Signal]
public delegate void DungeonFinishedGeneratingEventHandler();
[Dependency]
public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
public List<IDungeonFloor> Floors { get; set; } = default!;
private IDungeonFloor _currentFloor;
@@ -59,8 +58,5 @@ public partial class Map : Node3D, IMap
public Vector3 GetPlayerSpawnPosition() => _currentFloor.GetPlayerSpawnPoint();
private void OnTeleportEntered(Node3D body)
{
EmitSignal(SignalName.TeleportReached);
}
private void OnTeleportEntered(Node3D body) => GameEventDepot.OnTeleportEntered();
}

View File

@@ -1,6 +1,7 @@
[gd_scene load_steps=4 format=3 uid="uid://bc1sp6xwe0j65"]
[gd_scene load_steps=5 format=3 uid="uid://bc1sp6xwe0j65"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonGenerator3D.gd" id="1_sr15j"]
[ext_resource type="PackedScene" uid="uid://dpec2lbt83dhe" path="res://src/map/dungeon/scenes/Antechamber.tscn" id="3_hhw2n"]
[ext_resource type="PackedScene" uid="uid://bn4gslp2gk8ds" path="res://src/map/dungeon/corridor/Corridor.tscn" id="4_pgrs5"]
[ext_resource type="Script" path="res://src/map/dungeon/floors/DungeonFloor.cs" id="5_bsukb"]
@@ -10,8 +11,9 @@ script = ExtResource("5_bsukb")
[node name="DungeonGenerator" type="Node3D" parent="."]
unique_name_in_owner = true
script = ExtResource("1_sr15j")
room_scenes = Array[PackedScene]([ExtResource("3_hhw2n")])
corridor_room_scene = ExtResource("4_pgrs5")
dungeon_size = Vector3i(20, 1, 20)
voxel_scale = Vector3(32, 32, 32)
dungeon_size = Vector3i(40, 1, 40)
voxel_scale = Vector3(16, 16, 16)
generate_on_ready = false
place_even_if_fail = true

File diff suppressed because one or more lines are too long