feat: Implement a comprehensive global event bus with new event handlers and integrate player health and collection events.

This commit is contained in:
2026-01-31 15:15:52 +01:00
parent dde3eaa52e
commit 62bdf1ba39
15 changed files with 322 additions and 54 deletions

23
scripts/Constants.cs Normal file
View File

@@ -0,0 +1,23 @@
namespace Mr.BrickAdventures;
/// <summary>
/// Constants for autoload paths and other commonly used values.
/// </summary>
public static class Constants
{
// Autoload paths
public const string EventBusPath = "/root/EventBus";
public const string GameManagerPath = "/root/GameManager";
public const string SaveSystemPath = "/root/SaveSystem";
public const string SpeedRunManagerPath = "/root/SpeedRunManager";
public const string GhostManagerPath = "/root/GhostManager";
public const string AchievementManagerPath = "/root/AchievementManager";
public const string StatisticsManagerPath = "/root/StatisticsManager";
public const string SkillManagerPath = "/root/SkillManager";
public const string FloatingTextManagerPath = "/root/FloatingTextManager";
public const string UIManagerPath = "/root/UIManager";
public const string ConsoleManagerPath = "/root/ConsoleManager";
// Group names
public const string CoinsGroup = "coins";
}

1
scripts/Constants.cs.uid Normal file
View File

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

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
@@ -10,11 +11,10 @@ public partial class GhostEventHandler : Node
public override void _Ready()
{
_ghostManager = GetNode<GhostManager>("/root/GhostManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
eventBus.LevelStarted += OnLevelStarted;
eventBus.LevelCompleted += OnLevelCompleted;
_ghostManager = GetNode<GhostManager>(Constants.GhostManagerPath);
EventBus.Instance.LevelStarted += OnLevelStarted;
EventBus.Instance.LevelCompleted += OnLevelCompleted;
}
private void OnLevelStarted(int levelIndex, Node currentScene)
@@ -23,7 +23,7 @@ public partial class GhostEventHandler : Node
_ghostManager.StartRecording(levelIndex);
_ghostManager.SpawnGhostPlayer(levelIndex, currentScene);
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
_ghostManager.StopRecording(true, completionTime);

View File

@@ -0,0 +1,34 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles coin collection events and updates the session state.
/// Replaces the manual signal wiring in ScoreComponent.
/// </summary>
public partial class ScoreEventHandler : Node
{
private GameManager _gameManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>(Constants.GameManagerPath);
EventBus.Instance.CoinCollected += OnCoinCollected;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
EventBus.Instance.CoinCollected -= OnCoinCollected;
}
private void OnCoinCollected(int amount, Vector2 position)
{
var currentCoins = (int)_gameManager.CurrentSessionState["coins_collected"];
_gameManager.CurrentSessionState["coins_collected"] = currentCoins + amount;
GD.Print($"ScoreEventHandler: Collected {amount} coins. Total session coins: {currentCoins + amount}");
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
@@ -10,10 +11,9 @@ public partial class SpeedRunEventHandler : Node
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
eventBus.LevelCompleted += OnLevelCompleted;
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
EventBus.Instance.LevelCompleted += OnLevelCompleted;
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)

View File

@@ -0,0 +1,62 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles game events and updates statistics accordingly.
/// Listens to EventBus signals and increments relevant stats.
/// </summary>
public partial class StatisticsEventHandler : Node
{
private StatisticsManager _statisticsManager;
public override void _Ready()
{
_statisticsManager = GetNode<StatisticsManager>(Constants.StatisticsManagerPath);
// Subscribe to events
EventBus.Instance.CoinCollected += OnCoinCollected;
EventBus.Instance.EnemyDefeated += OnEnemyDefeated;
EventBus.Instance.PlayerDied += OnPlayerDied;
EventBus.Instance.LevelCompleted += OnLevelCompleted;
EventBus.Instance.ChildRescued += OnChildRescued;
}
public override void _ExitTree()
{
if (EventBus.Instance == null) return;
EventBus.Instance.CoinCollected -= OnCoinCollected;
EventBus.Instance.EnemyDefeated -= OnEnemyDefeated;
EventBus.Instance.PlayerDied -= OnPlayerDied;
EventBus.Instance.LevelCompleted -= OnLevelCompleted;
EventBus.Instance.ChildRescued -= OnChildRescued;
}
private void OnCoinCollected(int amount, Vector2 position)
{
_statisticsManager.IncrementStat("coins_collected", amount);
}
private void OnEnemyDefeated(Node enemy, Vector2 position)
{
_statisticsManager.IncrementStat("enemies_defeated");
}
private void OnPlayerDied(Vector2 position)
{
_statisticsManager.IncrementStat("deaths");
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
_statisticsManager.IncrementStat("levels_completed");
}
private void OnChildRescued(Vector2 position)
{
_statisticsManager.IncrementStat("children_rescued");
}
}

View File

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

View File

@@ -1,5 +1,6 @@
using System;
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
@@ -35,7 +36,7 @@ public partial class CollectableComponent : Node
if (Owner.HasNode("FadeAwayComponent"))
_hasFadeAway = true;
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
_floatingTextManager = GetNode<FloatingTextManager>(Constants.FloatingTextManagerPath);
}
private async void OnArea2DBodyEntered(Node2D body)
@@ -53,12 +54,18 @@ public partial class CollectableComponent : Node
{
case CollectableType.Coin:
_floatingTextManager?.ShowCoin((int)Data.Amount, ownerNode.GlobalPosition);
EventBus.EmitCoinCollected((int)Data.Amount, ownerNode.GlobalPosition);
break;
case CollectableType.Health:
_floatingTextManager?.ShowMessage("Healed!", ownerNode.GlobalPosition);
EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition);
break;
case CollectableType.Kid:
_floatingTextManager?.ShowMessage("Rescued!", ownerNode.GlobalPosition);
EventBus.EmitChildRescued(ownerNode.GlobalPosition);
break;
default:
EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition);
break;
}
}

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.components;
@@ -23,7 +24,7 @@ public partial class EnemyDeathComponent : Node
GD.PushError("EnemyDeathComponent: Health is not set.");
return;
}
Health.Death += OnDeath;
}
@@ -34,6 +35,12 @@ public partial class EnemyDeathComponent : Node
private async Task Die()
{
// Emit enemy defeated event for statistics and other systems
if (Owner is Node2D ownerNode)
{
EventBus.EmitEnemyDefeated(Owner, ownerNode.GlobalPosition);
}
CollisionShape.SetDisabled(true);
var tween = CreateTween();
tween.TweenProperty(Owner, "scale", Vector2.Zero, TweenDuration);

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.components;
@@ -11,22 +12,22 @@ public partial class HealthComponent : Node2D
[Export] public float MaxHealth { get; set; } = 1.0f;
[Export] public AudioStreamPlayer2D HurtSfx { get; set; }
[Export] public AudioStreamPlayer2D HealSfx { get; set; }
[Signal] public delegate void HealthChangedEventHandler(float delta, float totalHealth);
[Signal] public delegate void DeathEventHandler();
private FloatingTextManager _floatingTextManager;
public override void _Ready()
{
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
_floatingTextManager = GetNode<FloatingTextManager>(Constants.FloatingTextManagerPath);
}
public void SetHealth(float newValue)
{
_ = ApplyHealthChange(newValue);
}
public void IncreaseHealth(float delta)
{
_ = ApplyHealthChange(Health + delta);
@@ -46,7 +47,7 @@ public partial class HealthComponent : Node2D
if (delta == 0.0f)
return;
if (delta < 0.0f)
_floatingTextManager?.ShowDamage(Mathf.Abs(delta), GlobalPosition);
else
@@ -64,16 +65,27 @@ public partial class HealthComponent : Node2D
await HurtSfx.ToSignal(HurtSfx, AudioStreamPlayer2D.SignalName.Finished);
}
}
Health = newHealth;
if (Health <= 0f)
{
EmitSignalDeath();
// Emit global event if this is the player
if (Owner is PlayerController)
EventBus.EmitPlayerDied(GlobalPosition);
}
else
{
EmitSignalHealthChanged(delta, Health);
// Emit global events if this is the player
if (Owner is PlayerController)
{
if (delta < 0f)
EventBus.EmitPlayerDamaged(Mathf.Abs(delta), Health, GlobalPosition);
else
EventBus.EmitPlayerHealed(delta, Health, GlobalPosition);
}
}
}
}