refactor (#6)
Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
25
scripts/Constants.cs
Normal file
25
scripts/Constants.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
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 GameStateStorePath = "/root/GameStateStore";
|
||||
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";
|
||||
public const string ConfigFileHandlerPath = "/root/ConfigFileHandler";
|
||||
|
||||
// Group names
|
||||
public const string CoinsGroup = "coins";
|
||||
}
|
||||
1
scripts/Constants.cs.uid
Normal file
1
scripts/Constants.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bn7o3n3bomvrd
|
||||
29
scripts/Events/CoinStateHandler.cs
Normal file
29
scripts/Events/CoinStateHandler.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles coin collection events and updates the GameStateStore.
|
||||
/// Replaces the manual coin logic in GameManager.
|
||||
/// </summary>
|
||||
public partial class CoinStateHandler : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.Instance.CoinCollected += OnCoinCollected;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.CoinCollected -= OnCoinCollected;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCoinCollected(int amount, Vector2 position)
|
||||
{
|
||||
GameStateStore.Instance?.AddSessionCoins(amount);
|
||||
}
|
||||
}
|
||||
1
scripts/Events/CoinStateHandler.cs.uid
Normal file
1
scripts/Events/CoinStateHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://1qg3q53kkh0k
|
||||
@@ -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);
|
||||
|
||||
39
scripts/Events/LevelStateHandler.cs
Normal file
39
scripts/Events/LevelStateHandler.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles level completion events and updates GameStateStore.
|
||||
/// </summary>
|
||||
public partial class LevelStateHandler : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.Instance.LevelCompleted += OnLevelCompleted;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.LevelCompleted -= OnLevelCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
|
||||
{
|
||||
var store = GameStateStore.Instance;
|
||||
if (store == null) return;
|
||||
|
||||
// Mark level complete and unlock next
|
||||
store.MarkLevelComplete(levelIndex);
|
||||
|
||||
// Commit session data to persistent state
|
||||
store.CommitSessionCoins();
|
||||
store.CommitSessionSkills();
|
||||
|
||||
// Reset session for next level
|
||||
store.ResetSession();
|
||||
}
|
||||
}
|
||||
1
scripts/Events/LevelStateHandler.cs.uid
Normal file
1
scripts/Events/LevelStateHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://gx5vn7viphv
|
||||
29
scripts/Events/LivesStateHandler.cs
Normal file
29
scripts/Events/LivesStateHandler.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles player death events and updates lives in GameStateStore.
|
||||
/// </summary>
|
||||
public partial class LivesStateHandler : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.Instance.PlayerDied += OnPlayerDied;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.PlayerDied -= OnPlayerDied;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerDied(Vector2 position)
|
||||
{
|
||||
GameStateStore.Instance?.RemoveLife();
|
||||
GameStateStore.Instance?.ResetSession();
|
||||
}
|
||||
}
|
||||
1
scripts/Events/LivesStateHandler.cs.uid
Normal file
1
scripts/Events/LivesStateHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b4ocg7g8vmtvp
|
||||
43
scripts/Events/SkillCollectHandler.cs
Normal file
43
scripts/Events/SkillCollectHandler.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles skill collection events and unlocks skills via GameStateStore.
|
||||
/// Skills are immediately activated but only persisted on level complete.
|
||||
/// </summary>
|
||||
public partial class SkillCollectHandler : Node
|
||||
{
|
||||
private SkillManager _skillManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_skillManager = SkillManager.Instance;
|
||||
EventBus.Instance.SkillCollected += OnSkillCollected;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.SkillCollected -= OnSkillCollected;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSkillCollected(SkillData skill, Vector2 position)
|
||||
{
|
||||
if (skill == null) return;
|
||||
|
||||
// Unlock in session (will be committed on level complete, lost on death)
|
||||
GameStateStore.Instance?.UnlockSkillInSession(skill);
|
||||
|
||||
// Immediately activate the skill for the player
|
||||
skill.Level = 1;
|
||||
_skillManager?.AddSkill(skill);
|
||||
|
||||
// Emit skill unlocked event for UI/achievements
|
||||
EventBus.EmitSkillUnlocked(skill.Name, skill.Level);
|
||||
}
|
||||
}
|
||||
1
scripts/Events/SkillCollectHandler.cs.uid
Normal file
1
scripts/Events/SkillCollectHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c1po4hjvqbslm
|
||||
@@ -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)
|
||||
|
||||
62
scripts/Events/StatisticsEventHandler.cs
Normal file
62
scripts/Events/StatisticsEventHandler.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
1
scripts/Events/StatisticsEventHandler.cs.uid
Normal file
1
scripts/Events/StatisticsEventHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://l68tjau3k6bw
|
||||
@@ -6,4 +6,9 @@ public partial class CollectableResource : Resource
|
||||
{
|
||||
[Export] public float Amount { get; set; } = 0.0f;
|
||||
[Export] public CollectableType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The skill to unlock when collected. Only used when Type is Skill.
|
||||
/// </summary>
|
||||
[Export] public SkillData Skill { get; set; }
|
||||
}
|
||||
@@ -5,4 +5,5 @@ public enum CollectableType
|
||||
Coin,
|
||||
Kid,
|
||||
Health,
|
||||
Skill,
|
||||
}
|
||||
@@ -10,11 +10,10 @@ public partial class SkillData : Resource
|
||||
[Export] public string Name { get; set; } = "New Skill";
|
||||
[Export] public string Description { get; set; } = "New Skill";
|
||||
[Export] public Texture2D Icon { get; set; }
|
||||
[Export] public bool IsActive { get; set; } = false;
|
||||
[Export] public int Level { get; set; } = 1;
|
||||
[Export] public SkillType Type { get; set; } = SkillType.Throw;
|
||||
[Export] public PackedScene Node { get; set; }
|
||||
[Export] public Array<SkillUpgrade> Upgrades { get; set; } = [];
|
||||
|
||||
|
||||
public int MaxLevel => Upgrades.Count;
|
||||
}
|
||||
73
scripts/State/PlayerState.cs
Normal file
73
scripts/State/PlayerState.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.State;
|
||||
|
||||
/// <summary>
|
||||
/// Persistent player data that survives across sessions.
|
||||
/// This is a POCO (Plain Old C# Object) for predictable state management.
|
||||
/// </summary>
|
||||
public class PlayerState
|
||||
{
|
||||
/// <summary>
|
||||
/// Saved coins (not including current session).
|
||||
/// </summary>
|
||||
public int Coins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Remaining lives.
|
||||
/// </summary>
|
||||
public int Lives { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Indices of completed levels.
|
||||
/// </summary>
|
||||
public List<int> CompletedLevels { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Indices of levels the player can access.
|
||||
/// </summary>
|
||||
public List<int> UnlockedLevels { get; set; } = new() { 0 };
|
||||
|
||||
/// <summary>
|
||||
/// Skills the player has permanently unlocked.
|
||||
/// </summary>
|
||||
public List<SkillData> UnlockedSkills { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Statistics dictionary for tracking game stats.
|
||||
/// </summary>
|
||||
public Dictionary<string, int> Statistics { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// IDs of unlocked achievements.
|
||||
/// </summary>
|
||||
public List<string> UnlockedAchievements { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fresh default player state.
|
||||
/// </summary>
|
||||
public static PlayerState CreateDefault() => new()
|
||||
{
|
||||
Coins = 0,
|
||||
Lives = 3,
|
||||
CompletedLevels = new List<int>(),
|
||||
UnlockedLevels = new List<int> { 0 },
|
||||
UnlockedSkills = new List<SkillData>(),
|
||||
Statistics = new Dictionary<string, int>()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Resets this state to default values.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Coins = 0;
|
||||
Lives = 3;
|
||||
CompletedLevels.Clear();
|
||||
UnlockedLevels.Clear();
|
||||
UnlockedLevels.Add(0);
|
||||
UnlockedSkills.Clear();
|
||||
Statistics.Clear();
|
||||
}
|
||||
}
|
||||
1
scripts/State/PlayerState.cs.uid
Normal file
1
scripts/State/PlayerState.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://gtr1e60jq7iv
|
||||
55
scripts/State/SessionState.cs
Normal file
55
scripts/State/SessionState.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.State;
|
||||
|
||||
/// <summary>
|
||||
/// Data for the current gameplay session.
|
||||
/// Reset when player dies or completes a level.
|
||||
/// </summary>
|
||||
public class SessionState
|
||||
{
|
||||
/// <summary>
|
||||
/// Current level index being played.
|
||||
/// </summary>
|
||||
public int CurrentLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Coins collected during this session (not yet saved).
|
||||
/// </summary>
|
||||
public int CoinsCollected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skills unlocked during this session (not yet saved).
|
||||
/// </summary>
|
||||
public List<SkillData> SkillsUnlocked { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fresh session state.
|
||||
/// </summary>
|
||||
public static SessionState CreateDefault() => new()
|
||||
{
|
||||
CurrentLevel = 0,
|
||||
CoinsCollected = 0,
|
||||
SkillsUnlocked = new List<SkillData>()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Resets session state to defaults.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
CoinsCollected = 0;
|
||||
SkillsUnlocked.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets completely including level.
|
||||
/// </summary>
|
||||
public void ResetAll()
|
||||
{
|
||||
CurrentLevel = 0;
|
||||
CoinsCollected = 0;
|
||||
SkillsUnlocked.Clear();
|
||||
}
|
||||
}
|
||||
1
scripts/State/SessionState.cs.uid
Normal file
1
scripts/State/SessionState.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chqsdleqrnl7b
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -10,19 +11,19 @@ public partial class AudioSettings : Control
|
||||
[Export] public Slider SfxVolumeSlider { get; set; }
|
||||
[Export] public Control AudioSettingsControl { get; set; }
|
||||
[Export] public float MuteThreshold { get; set; } = -20f;
|
||||
|
||||
|
||||
private UIManager _uiManager;
|
||||
private ConfigFileHandler _configFileHandler;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
_configFileHandler = GetNode<ConfigFileHandler>("/root/ConfigFileHandler");
|
||||
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
|
||||
_configFileHandler = GetNode<ConfigFileHandler>(Constants.ConfigFileHandlerPath);
|
||||
Initialize();
|
||||
MasterVolumeSlider.ValueChanged += OnMasterVolumeChanged;
|
||||
MusicVolumeSlider.ValueChanged += OnMusicVolumeChanged;
|
||||
SfxVolumeSlider.ValueChanged += OnSfxVolumeChanged;
|
||||
|
||||
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
@@ -35,7 +36,7 @@ public partial class AudioSettings : Control
|
||||
{
|
||||
if (!@event.IsActionReleased("ui_cancel")) return;
|
||||
if (!_uiManager.IsScreenOnTop(AudioSettingsControl)) return;
|
||||
|
||||
|
||||
SaveSettings();
|
||||
_uiManager.PopScreen();
|
||||
}
|
||||
@@ -64,12 +65,12 @@ public partial class AudioSettings : Control
|
||||
MasterVolumeSlider.Value = volumeDb;
|
||||
MasterVolumeSlider.MinValue = MuteThreshold;
|
||||
MasterVolumeSlider.MaxValue = 0f;
|
||||
|
||||
|
||||
var musicVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("music"));
|
||||
MusicVolumeSlider.Value = musicVolumeDb;
|
||||
MusicVolumeSlider.MinValue = MuteThreshold;
|
||||
MusicVolumeSlider.MaxValue = 0f;
|
||||
|
||||
|
||||
var sfxVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("sfx"));
|
||||
SfxVolumeSlider.Value = sfxVolumeDb;
|
||||
SfxVolumeSlider.MinValue = MuteThreshold;
|
||||
@@ -95,12 +96,12 @@ public partial class AudioSettings : Control
|
||||
{
|
||||
var settingsConfig = _configFileHandler.SettingsConfig;
|
||||
if (!settingsConfig.HasSection("audio_settings")) return;
|
||||
|
||||
|
||||
var masterVolume = (float)settingsConfig.GetValue("audio_settings", "master_volume", MasterVolumeSlider.Value);
|
||||
var musicVolume = (float)settingsConfig.GetValue("audio_settings", "music_volume", MusicVolumeSlider.Value);
|
||||
var sfxVolume = (float)settingsConfig.GetValue("audio_settings", "sfx_volume", SfxVolumeSlider.Value);
|
||||
var muteThreshold = (float)settingsConfig.GetValue("audio_settings", "mute_threshold", MuteThreshold);
|
||||
|
||||
|
||||
MasterVolumeSlider.Value = masterVolume;
|
||||
MusicVolumeSlider.Value = musicVolume;
|
||||
SfxVolumeSlider.Value = sfxVolume;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
@@ -17,15 +18,15 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
{
|
||||
ProgressBar.Hide();
|
||||
|
||||
_skillManager = GetNodeOrNull<SkillManager>("/root/SkillManager");
|
||||
_skillManager = GetNodeOrNull<SkillManager>(Constants.SkillManagerPath);
|
||||
if (_skillManager == null)
|
||||
{
|
||||
GD.PrintErr("ChargeProgressBar: SkillManager autoload not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_skillManager.ActiveThrowSkillChanged += OnActiveThrowSkillChanged;
|
||||
|
||||
|
||||
SetupDependencies();
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
OnOwnerExiting();
|
||||
|
||||
if (throwComponent == null || !IsInstanceValid(throwComponent)) return;
|
||||
|
||||
|
||||
_throwComponent = throwComponent;
|
||||
_throwComponent.TreeExiting += OnOwnerExiting;
|
||||
SetupDependencies();
|
||||
@@ -60,7 +61,7 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
}
|
||||
_throwComponent = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void SetupDependencies()
|
||||
{
|
||||
@@ -68,7 +69,7 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_throwComponent.ThrowInputBehavior is ChargeThrowInputResource throwInput)
|
||||
{
|
||||
_throwInput = throwInput;
|
||||
@@ -77,7 +78,7 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
{
|
||||
_throwInput = null;
|
||||
}
|
||||
|
||||
|
||||
if (_throwInput == null)
|
||||
{
|
||||
return;
|
||||
@@ -88,9 +89,9 @@ public partial class ChargeProgressBar : ProgressBar
|
||||
ProgressBar.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SetupProgressBar();
|
||||
|
||||
|
||||
_throwInput.ChargeStarted += OnChargeStarted;
|
||||
_throwInput.ChargeStopped += OnChargeStopped;
|
||||
_throwInput.ChargeUpdated += OnChargeUpdated;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -9,7 +10,7 @@ public partial class Credits : Control
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
using Mr.BrickAdventures.scripts.State;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
@@ -12,27 +14,44 @@ public partial class DeathScreen : Control
|
||||
[Export] public Label LivesLeftLabel { get; set; }
|
||||
[Export] public float TimeoutTime { get; set; } = 2.0f;
|
||||
[Export] public Godot.Collections.Array<Node> NodesToDisable { get; set; } = new();
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
private Timer _timer;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
SetLabels();
|
||||
_gameManager = GameManager.Instance;
|
||||
|
||||
// Subscribe to lives changed event for reactive updates
|
||||
EventBus.Instance.LivesChanged += OnLivesChanged;
|
||||
}
|
||||
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.LivesChanged -= OnLivesChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLivesChanged(int lives)
|
||||
{
|
||||
// Update the label when lives change
|
||||
LivesLeftLabel.Text = $" x {lives}";
|
||||
}
|
||||
|
||||
private void SetLabels()
|
||||
{
|
||||
if (_gameManager == null) return;
|
||||
|
||||
if (CurrentLevel != null)
|
||||
{
|
||||
CurrentLevelLabel.Text = CurrentLevel.LevelName;
|
||||
}
|
||||
LivesLeftLabel.Text = $" x {_gameManager.GetLives()}";
|
||||
|
||||
// Read current lives from store
|
||||
var lives = GameStateStore.Instance?.Player.Lives ?? 0;
|
||||
LivesLeftLabel.Text = $" x {lives}";
|
||||
}
|
||||
|
||||
|
||||
private void SetupTimer()
|
||||
{
|
||||
_timer = new Timer();
|
||||
@@ -42,31 +61,32 @@ public partial class DeathScreen : Control
|
||||
AddChild(_timer);
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
|
||||
private void ToggleNodes()
|
||||
{
|
||||
foreach (var node in NodesToDisable)
|
||||
{
|
||||
node.ProcessMode = node.ProcessMode == ProcessModeEnum.Disabled
|
||||
? ProcessModeEnum.Inherit
|
||||
node.ProcessMode = node.ProcessMode == ProcessModeEnum.Disabled
|
||||
? ProcessModeEnum.Inherit
|
||||
: ProcessModeEnum.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnPlayerDeath()
|
||||
{
|
||||
if (_gameManager == null) return;
|
||||
|
||||
|
||||
ToggleNodes();
|
||||
SetLabels();
|
||||
Show();
|
||||
SetupTimer();
|
||||
}
|
||||
|
||||
|
||||
private void OnTimeout()
|
||||
{
|
||||
if (_gameManager == null || _gameManager.GetLives() == 0) return;
|
||||
|
||||
var lives = GameStateStore.Instance?.Player.Lives ?? 0;
|
||||
if (lives == 0) return;
|
||||
|
||||
GetTree().ReloadCurrentScene();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -9,14 +10,14 @@ public partial class GameOverScreen : Control
|
||||
[Export] public Button RestartButton { get; set; }
|
||||
[Export] public Button MainMenuButton { get; set; }
|
||||
[Export] public PackedScene MainMenuScene { get; set; }
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
RestartButton.Pressed += OnRestartClicked;
|
||||
MainMenuButton.Pressed += OnMainMenuClicked;
|
||||
MainMenuButton.Pressed += OnMainMenuClicked;
|
||||
}
|
||||
|
||||
private void OnMainMenuClicked()
|
||||
@@ -33,7 +34,7 @@ public partial class GameOverScreen : Control
|
||||
public void OnPlayerDeath()
|
||||
{
|
||||
if (_gameManager == null || _gameManager.GetLives() != 0) return;
|
||||
|
||||
|
||||
GameOverPanel.Show();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
|
||||
@@ -15,7 +16,7 @@ public partial class Hud : Control
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -14,16 +15,16 @@ public partial class MainMenu : Control
|
||||
[Export] public Label VersionLabel { get; set; }
|
||||
[Export] public Control SettingsControl { get; set; }
|
||||
[Export] public Control CreditsControl { get; set; }
|
||||
|
||||
|
||||
private SaveSystem _saveSystem;
|
||||
private GameManager _gameManager;
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_saveSystem = GetNode<SaveSystem>("/root/SaveSystem");
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
_saveSystem = SaveSystem.Instance;
|
||||
_gameManager = GameManager.Instance;
|
||||
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
|
||||
|
||||
NewGameButton.Pressed += OnNewGamePressed;
|
||||
ContinueButton.Pressed += OnContinuePressed;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
@@ -17,7 +18,7 @@ public partial class Marketplace : Control
|
||||
[Export] public Array<Node> ComponentsToDisable { get; set; } = [];
|
||||
[Export] public PackedScene MarketplaceButtonScene { get; set; }
|
||||
[Export] public PackedScene SkillButtonScene { get; set; }
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
private SkillManager _skillManager;
|
||||
private readonly List<Button> _unlockButtons = [];
|
||||
@@ -25,33 +26,37 @@ public partial class Marketplace : Control
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_skillManager = GetNode<SkillManager>("/root/SkillManager");
|
||||
_skillManager.SkillRemoved += OnSkillRemoved;
|
||||
|
||||
_gameManager = GameManager.Instance;
|
||||
_skillManager = SkillManager.Instance;
|
||||
|
||||
Skills = _skillManager.AvailableSkills;
|
||||
|
||||
|
||||
var skillsToUnlock = new List<SkillData>();
|
||||
|
||||
|
||||
foreach (var skill in Skills) skillsToUnlock.Add(skill);
|
||||
|
||||
foreach (var skill in skillsToUnlock) CreateUpgradeButton(skill);
|
||||
|
||||
var unlockedSkills = _gameManager.GetUnlockedSkills();
|
||||
foreach (var skill in unlockedSkills) CreateSkillButton(skill);
|
||||
|
||||
|
||||
SkillUnlockerComponent.SkillUnlocked += OnSkillUnlocked;
|
||||
EventBus.Instance.SkillCollected += OnGlobalSkillCollected;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
SkillUnlockerComponent.SkillUnlocked -= OnSkillUnlocked;
|
||||
if (EventBus.Instance != null)
|
||||
{
|
||||
EventBus.Instance.SkillCollected -= OnGlobalSkillCollected;
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (!@event.IsActionPressed("show_marketplace")) return;
|
||||
|
||||
|
||||
if (IsVisible())
|
||||
{
|
||||
Hide();
|
||||
@@ -75,12 +80,12 @@ public partial class Marketplace : Control
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!buttonExists) CreateSkillButton(skill);
|
||||
|
||||
foreach (var btn in _skillButtons)
|
||||
{
|
||||
if (btn.Data.IsActive) btn.Activate();
|
||||
if (_skillManager.IsSkillActive(btn.Data)) btn.Activate();
|
||||
else btn.Deactivate();
|
||||
}
|
||||
}
|
||||
@@ -92,7 +97,7 @@ public partial class Marketplace : Control
|
||||
button.Setup();
|
||||
button.Pressed += () => OnSkillButtonPressed(button);
|
||||
button.Activate();
|
||||
|
||||
|
||||
_skillButtons.Add(button);
|
||||
UnlockedGrid.AddChild(button);
|
||||
UnlockedGrid.QueueSort();
|
||||
@@ -104,7 +109,7 @@ public partial class Marketplace : Control
|
||||
button.Data = skill;
|
||||
button.Icon = skill.Icon;
|
||||
button.Pressed += () => OnUpgradeButtonPressed(skill);
|
||||
|
||||
|
||||
_unlockButtons.Add(button);
|
||||
ToUnlockGrid.AddChild(button);
|
||||
ToUnlockGrid.QueueSort();
|
||||
@@ -117,7 +122,7 @@ public partial class Marketplace : Control
|
||||
if (skill.Level < skill.MaxLevel)
|
||||
{
|
||||
SkillUnlockerComponent.TryUpgradeSkill(skill);
|
||||
if (!skill.IsActive) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill);
|
||||
if (!SkillUnlockerComponent.SkillManager.IsSkillActive(skill)) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -133,31 +138,18 @@ public partial class Marketplace : Control
|
||||
private void OnSkillButtonPressed(SkillButton button)
|
||||
{
|
||||
SkillUnlockerComponent.SkillManager.ToggleSkillActivation(button.Data);
|
||||
|
||||
|
||||
foreach (var btn in _skillButtons)
|
||||
{
|
||||
if (btn.Data.IsActive)
|
||||
if (SkillUnlockerComponent.SkillManager.IsSkillActive(btn.Data))
|
||||
btn.Activate();
|
||||
else
|
||||
btn.Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSkillRemoved(SkillData skill)
|
||||
|
||||
private void OnGlobalSkillCollected(SkillData skill, Vector2 position)
|
||||
{
|
||||
SkillButton buttonToRemove = null;
|
||||
foreach (var button in _skillButtons)
|
||||
{
|
||||
if (button.Data == skill)
|
||||
{
|
||||
buttonToRemove = button;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buttonToRemove != null)
|
||||
{
|
||||
_skillButtons.Remove(buttonToRemove);
|
||||
buttonToRemove.QueueFree();
|
||||
}
|
||||
OnSkillUnlocked(skill);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
@@ -11,26 +12,26 @@ public partial class MarketplaceButton : Button
|
||||
[Export] public Texture2D UnlockedSkillIcon { get; set; }
|
||||
[Export] public Texture2D LockedSkillIcon { get; set; }
|
||||
[Export] public Container SkillLevelContainer { get; set; }
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
private SkillUnlockerComponent _skillUnlockerComponent;
|
||||
private SkillManager _skillManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
var player = _gameManager.Player;
|
||||
if (player == null) return;
|
||||
|
||||
|
||||
_skillUnlockerComponent = player.GetNodeOrNull<SkillUnlockerComponent>("SkillUnlockerComponent");
|
||||
if (_skillUnlockerComponent != null)
|
||||
{
|
||||
_skillUnlockerComponent.SkillUnlocked += OnSkillStateChanged;
|
||||
}
|
||||
|
||||
_skillManager = GetNode<SkillManager>("/root/SkillManager");
|
||||
|
||||
_skillManager = SkillManager.Instance;
|
||||
_skillManager.SkillRemoved += OnSkillStateChanged;
|
||||
|
||||
|
||||
UpdateButtonState();
|
||||
}
|
||||
|
||||
@@ -41,7 +42,7 @@ public partial class MarketplaceButton : Button
|
||||
_skillUnlockerComponent.SkillUnlocked -= OnSkillStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnSkillStateChanged(SkillData skill)
|
||||
{
|
||||
if (skill.Name == Data.Name)
|
||||
@@ -59,7 +60,7 @@ public partial class MarketplaceButton : Button
|
||||
}
|
||||
|
||||
var isUnlocked = _gameManager.IsSkillUnlocked(Data);
|
||||
|
||||
|
||||
for (var i = 0; i < SkillLevelContainer.GetChildCount(); i++)
|
||||
{
|
||||
SkillLevelContainer.GetChild(i).QueueFree();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -18,8 +19,8 @@ public partial class PauseMenu : Control
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
|
||||
|
||||
ResumeButton.Pressed += OnResumePressed;
|
||||
MainMenuButton.Pressed += OnMainMenuPressed;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -14,18 +15,18 @@ public partial class SettingsMenu : Control
|
||||
[Export] public Button AudioSettingsButton { get; set; }
|
||||
[Export] public Button DisplaySettingsButton { get; set; }
|
||||
[Export] public Button GameplaySettingsButton { get; set; }
|
||||
|
||||
|
||||
private UIManager _uiManager;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
|
||||
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
|
||||
|
||||
InputSettingsButton.Pressed += OnInputSettingsPressed;
|
||||
AudioSettingsButton.Pressed += OnAudioSettingsPressed;
|
||||
DisplaySettingsButton.Pressed += OnDisplaySettingsPressed;
|
||||
GameplaySettingsButton.Pressed += OnGameplaySettingsPressed;
|
||||
|
||||
|
||||
InputSettingsControl?.Hide();
|
||||
AudioSettingsControl?.Hide();
|
||||
DisplaySettingsControl?.Hide();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
@@ -7,15 +8,15 @@ namespace Mr.BrickAdventures.scripts.UI;
|
||||
public partial class SpeedRunHud : Control
|
||||
{
|
||||
[Export] private Label _timerLabel;
|
||||
|
||||
|
||||
private SpeedRunManager _speedRunManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
|
||||
|
||||
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
|
||||
|
||||
_speedRunManager.TimeUpdated += OnTimerUpdated;
|
||||
|
||||
|
||||
Visible = _speedRunManager.IsVisible;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,26 +5,27 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class BrickArmorSkillComponent : Node, ISkill
|
||||
public partial class BrickArmorSkillComponent : SkillComponentBase
|
||||
{
|
||||
private HealthComponent _healthComponent;
|
||||
private SkillData _skillData;
|
||||
private float _armorBonus = 0;
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
|
||||
public override void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
if (owner is not PlayerController player) return;
|
||||
_healthComponent = player.GetNode<HealthComponent>("HealthComponent");
|
||||
_skillData = data;
|
||||
base.Initialize(owner, data);
|
||||
if (Player != null)
|
||||
{
|
||||
_healthComponent = Player.GetNode<HealthComponent>("HealthComponent");
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
public override void Activate()
|
||||
{
|
||||
if (_healthComponent == null || _skillData == null) return;
|
||||
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
|
||||
if (_healthComponent == null || Data == null) return;
|
||||
ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (_healthComponent == null) return;
|
||||
_healthComponent.MaxHealth -= _armorBonus;
|
||||
@@ -35,7 +36,7 @@ public partial class BrickArmorSkillComponent : Node, ISkill
|
||||
_armorBonus = 0;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
public override void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
if (_healthComponent == null || upgrade == null) return;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
@@ -6,41 +7,33 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class BrickShieldSkillComponent : Node, ISkill
|
||||
public partial class BrickShieldSkillComponent : SkillComponentBase
|
||||
{
|
||||
[Export] public PackedScene ShieldScene { get; set; }
|
||||
|
||||
private PlayerController _player;
|
||||
|
||||
private Node2D _shieldInstance;
|
||||
private SkillData _skillData;
|
||||
private GameManager _gameManager;
|
||||
private SkillManager _skillManager;
|
||||
private HealthComponent _shieldHealth;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_skillManager = GetNode<SkillManager>("/root/SkillManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
_skillManager = SkillManager.Instance;
|
||||
}
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
public override void Activate()
|
||||
{
|
||||
_player = owner as PlayerController;
|
||||
_skillData = data;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (_player == null || ShieldScene == null || _shieldInstance != null) return;
|
||||
if (Player == null || ShieldScene == null || _shieldInstance != null) return;
|
||||
|
||||
_shieldInstance = ShieldScene.Instantiate<Node2D>();
|
||||
_player.AddChild(_shieldInstance);
|
||||
Player.AddChild(_shieldInstance);
|
||||
_shieldInstance.Position = Vector2.Zero;
|
||||
_shieldInstance.TreeExiting += OnShieldDestroyed;
|
||||
_shieldHealth = _shieldInstance.GetNode<HealthComponent>("HealthComponent");
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (_shieldInstance != null && IsInstanceValid(_shieldInstance))
|
||||
{
|
||||
@@ -50,7 +43,7 @@ public partial class BrickShieldSkillComponent : Node, ISkill
|
||||
_shieldInstance = null;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
public override void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
upgrade.Properties.TryGetValue("shield_health", out var newHealth);
|
||||
if (_shieldHealth != null)
|
||||
@@ -59,13 +52,13 @@ public partial class BrickShieldSkillComponent : Node, ISkill
|
||||
_shieldHealth.MaxHealth = (float)newHealth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnShieldDestroyed()
|
||||
{
|
||||
if (_gameManager != null && _skillData != null && _skillManager != null)
|
||||
if (_gameManager != null && Data != null && _skillManager != null)
|
||||
{
|
||||
_gameManager.RemoveSkill(_skillData.Name);
|
||||
_skillManager.RemoveSkill(_skillData.Name);
|
||||
_gameManager.RemoveSkill(Data.Name);
|
||||
_skillManager.RemoveSkill(Data.Name);
|
||||
}
|
||||
_shieldInstance = null;
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class BrickThrowComponent : Node, ISkill
|
||||
public partial class BrickThrowComponent : SkillComponentBase
|
||||
{
|
||||
[Export] public PackedScene BrickScene { get; set; }
|
||||
[Export] public float FireRate { get; set; } = 1.0f;
|
||||
[Export] public PlayerController PlayerController { get; set; }
|
||||
[Export] public ThrowInputResource ThrowInputBehavior { get; set; }
|
||||
|
||||
|
||||
private bool _canThrow = true;
|
||||
private Timer _timer;
|
||||
private SkillData _skillData;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
@@ -59,60 +57,54 @@ public partial class BrickThrowComponent : Node, ISkill
|
||||
|
||||
private void ThrowBrick(float powerMultiplier = 1f)
|
||||
{
|
||||
if (!_canThrow || PlayerController == null || BrickScene == null)
|
||||
if (!_canThrow || Player == null || BrickScene == null)
|
||||
return;
|
||||
|
||||
var instance = BrickScene.Instantiate<Node2D>();
|
||||
var init = instance.GetNodeOrNull<ProjectileInitComponent>("ProjectileInitComponent");
|
||||
|
||||
|
||||
if (init != null)
|
||||
{
|
||||
var @params = new ProjectileInitParams()
|
||||
{
|
||||
Position = PlayerController.GlobalPosition,
|
||||
Rotation = PlayerController.Rotation,
|
||||
Direction = PlayerController.LastDirection,
|
||||
Position = Player.GlobalPosition,
|
||||
Rotation = Player.Rotation,
|
||||
Direction = Player.LastDirection,
|
||||
PowerMultiplier = powerMultiplier,
|
||||
};
|
||||
|
||||
|
||||
init.Initialize(@params);
|
||||
}
|
||||
|
||||
|
||||
GetTree().CurrentScene.AddChild(instance);
|
||||
_canThrow = false;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
public override void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
PlayerController = owner as PlayerController;
|
||||
_skillData = data;
|
||||
base.Initialize(owner, data);
|
||||
|
||||
ThrowInputBehavior = (ThrowInputResource)ThrowInputBehavior?.Duplicate();
|
||||
|
||||
if (PlayerController == null)
|
||||
if (Data.Level > 0 && Data.Upgrades.Count >= Data.Level)
|
||||
{
|
||||
GD.PushError("BrickThrowComponent: Owner is not a PlayerController.");
|
||||
}
|
||||
|
||||
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
|
||||
{
|
||||
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
|
||||
ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
public override void Activate()
|
||||
{
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
|
||||
SetProcessInput(true);
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
public override void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
foreach (var property in upgrade.Properties)
|
||||
{
|
||||
|
||||
@@ -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,25 @@ 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;
|
||||
case CollectableType.Skill:
|
||||
if (Data.Skill != null)
|
||||
{
|
||||
_floatingTextManager?.ShowMessage($"{Data.Skill.Name} Unlocked!", ownerNode.GlobalPosition);
|
||||
EventBus.EmitSkillCollected(Data.Skill, ownerNode.GlobalPosition);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,25 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class DoubleJumpSkillComponent : Node, ISkill
|
||||
public partial class DoubleJumpSkillComponent : SkillComponentBase
|
||||
{
|
||||
[Export] private PackedScene _doubleJumpAbilityScene;
|
||||
private PlayerController _playerController;
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
_playerController = owner as PlayerController;
|
||||
if (_playerController == null)
|
||||
{
|
||||
GD.PrintErr("DoubleJumpSkillComponent must be a child of a PlayerController.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
public override void Activate()
|
||||
{
|
||||
if (_playerController == null) return;
|
||||
if (Player == null) return;
|
||||
|
||||
var hasAbility = _playerController.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
|
||||
var hasAbility = Player.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
|
||||
|
||||
if (!hasAbility)
|
||||
{
|
||||
var abilityInstance = _doubleJumpAbilityScene.Instantiate<DoubleJumpAbility>();
|
||||
_playerController.AddAbility(abilityInstance);
|
||||
Player.AddAbility(abilityInstance);
|
||||
}
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
_playerController?.RemoveAbility<DoubleJumpAbility>();
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
|
||||
Player?.RemoveAbility<DoubleJumpAbility>();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.State;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
@@ -12,28 +14,30 @@ public partial class ExitDoorComponent : Area2D, IUnlockable
|
||||
[Export] public AudioStreamPlayer2D OpenDoorSfx { get; set; }
|
||||
[Export] public int OpenedDoorFrame { get; set; } = 0;
|
||||
[Export] public string AchievementId = "level_complete_1";
|
||||
|
||||
|
||||
[Signal] public delegate void ExitTriggeredEventHandler();
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
private AchievementManager _achievementManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_achievementManager = GetNode<AchievementManager>("/root/AchievementManager");
|
||||
|
||||
_gameManager = GameManager.Instance;
|
||||
_achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath);
|
||||
|
||||
BodyEntered += OnExitAreaBodyEntered;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void OnExitAreaBodyEntered(Node2D body)
|
||||
{
|
||||
if (Locked) return;
|
||||
|
||||
|
||||
EmitSignalExitTriggered();
|
||||
_achievementManager.UnlockAchievement(AchievementId);
|
||||
_gameManager.UnlockLevel((int)_gameManager.PlayerState["current_level"] + 1);
|
||||
// Get current level from GameStateStore
|
||||
var currentLevel = GameStateStore.Instance?.Session.CurrentLevel ?? 0;
|
||||
_gameManager.UnlockLevel(currentLevel + 1);
|
||||
CallDeferred(nameof(GoToNextLevel));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,62 +5,59 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class GroundPoundSkillComponent : Node, ISkill
|
||||
public partial class GroundPoundSkillComponent : SkillComponentBase
|
||||
{
|
||||
[Export] public float PoundForce { get; set; } = 1200f;
|
||||
[Export] public PackedScene ShockwaveScene { get; set; }
|
||||
|
||||
private PlayerController _player;
|
||||
|
||||
private PlayerInputHandler _input;
|
||||
private bool _isPounding = false;
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
|
||||
public override void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
_player = owner as PlayerController;
|
||||
if (_player != null)
|
||||
base.Initialize(owner, data);
|
||||
if (Player != null)
|
||||
{
|
||||
_input = _player.GetNode<PlayerInputHandler>("PlayerInputHandler");
|
||||
_input = Player.GetNode<PlayerInputHandler>("PlayerInputHandler");
|
||||
}
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_player == null || _input == null)
|
||||
if (Player == null || _input == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if we just landed from a ground pound to create the shockwave.
|
||||
if (_isPounding && _player.IsOnFloor())
|
||||
if (_isPounding && Player.IsOnFloor())
|
||||
{
|
||||
_isPounding = false;
|
||||
if (ShockwaveScene != null)
|
||||
{
|
||||
var shockwave = ShockwaveScene.Instantiate<Node2D>();
|
||||
_player.GetParent()?.AddChild(shockwave);
|
||||
shockwave.GlobalPosition = _player.GlobalPosition;
|
||||
Player.GetParent()?.AddChild(shockwave);
|
||||
shockwave.GlobalPosition = Player.GlobalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to initiate a ground pound. The player must be in the air.
|
||||
if (_input.DownHeld && !_player.IsOnFloor() && !_isPounding)
|
||||
if (_input.DownHeld && !Player.IsOnFloor() && !_isPounding)
|
||||
{
|
||||
// Apply a strong downward force, zeroing out horizontal movement.
|
||||
_player.Velocity = new Vector2(0, PoundForce);
|
||||
Player.Velocity = new Vector2(0, PoundForce);
|
||||
_isPounding = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
|
||||
public override void Activate()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
|
||||
public override void Deactivate()
|
||||
{
|
||||
SetPhysicsProcess(false);
|
||||
_isPounding = false;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade) { }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
@@ -15,25 +16,25 @@ public partial class LeverComponent : Node
|
||||
|
||||
[Signal]
|
||||
public delegate void ActivatedEventHandler();
|
||||
|
||||
|
||||
private FloatingTextManager _floatingTextManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
|
||||
|
||||
_floatingTextManager = GetNode<FloatingTextManager>(Constants.FloatingTextManagerPath);
|
||||
|
||||
if (Area == null)
|
||||
{
|
||||
GD.PushError("LeverComponent: Area is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Sprite == null)
|
||||
{
|
||||
GD.PushError("LeverComponent: Sprite is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Area.BodyEntered += OnBodyEntered;
|
||||
Area.AreaEntered += OnAreaEntered;
|
||||
}
|
||||
@@ -58,7 +59,7 @@ public partial class LeverComponent : Node
|
||||
await timer.ToSignal(timer, Timer.SignalName.Timeout);
|
||||
Sprite.Frame = StartAnimationIndex;
|
||||
}
|
||||
|
||||
|
||||
private void HandleTriggerLogic(Node2D obj)
|
||||
{
|
||||
var triggerLever = obj.GetNodeOrNull<TriggerLeverComponent>("TriggerLeverComponent");
|
||||
|
||||
@@ -7,14 +7,12 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class MagneticSkillComponent : Node, ISkill
|
||||
public partial class MagneticSkillComponent : SkillComponentBase
|
||||
{
|
||||
[Export] public Area2D MagneticArea { get; set; }
|
||||
[Export] public float MagneticMoveDuration { get; set; } = 1.25f;
|
||||
|
||||
private Array<Node2D> _collectablesToPickUp = [];
|
||||
private Node2D _owner;
|
||||
private SkillData _skillData;
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
@@ -25,11 +23,11 @@ public partial class MagneticSkillComponent : Node, ISkill
|
||||
_collectablesToPickUp.Remove(collectable);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
MoveCollectableToOwner(collectable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
{
|
||||
if (!HasComponentInChildren(body, "CollectableComponent")) return;
|
||||
@@ -37,11 +35,11 @@ public partial class MagneticSkillComponent : Node, ISkill
|
||||
if (_collectablesToPickUp.Contains(body)) return;
|
||||
_collectablesToPickUp.Add(body);
|
||||
}
|
||||
|
||||
|
||||
private void OnAreaEntered(Area2D area)
|
||||
{
|
||||
if (!HasComponentInChildren(area, "CollectableComponent")) return;
|
||||
|
||||
|
||||
if (_collectablesToPickUp.Contains(area)) return;
|
||||
_collectablesToPickUp.Add(area);
|
||||
}
|
||||
@@ -49,7 +47,7 @@ public partial class MagneticSkillComponent : Node, ISkill
|
||||
private bool HasComponentInChildren(Node node, string componentName)
|
||||
{
|
||||
if (node == null) return false;
|
||||
|
||||
|
||||
if (node.HasNode(componentName)) return true;
|
||||
|
||||
foreach (var child in node.GetChildren())
|
||||
@@ -59,28 +57,27 @@ public partial class MagneticSkillComponent : Node, ISkill
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void MoveCollectableToOwner(Node2D collectable)
|
||||
{
|
||||
if (!IsInstanceValid(collectable) || !IsInstanceValid(_owner)) return;
|
||||
|
||||
var direction = (_owner.GlobalPosition - collectable.GlobalPosition).Normalized();
|
||||
if (!IsInstanceValid(collectable) || !IsInstanceValid(Player)) return;
|
||||
|
||||
var direction = (Player.GlobalPosition - collectable.GlobalPosition).Normalized();
|
||||
var speed = direction.Length() / MagneticMoveDuration;
|
||||
|
||||
collectable.GlobalPosition += direction.Normalized() * speed;
|
||||
}
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
public override void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
_owner = owner as Node2D;
|
||||
_skillData = data;
|
||||
|
||||
if (_owner == null)
|
||||
base.Initialize(owner, data);
|
||||
|
||||
if (Player == null)
|
||||
{
|
||||
GD.PushWarning("MagneticSkillComponent: Owner is not a Node2D.");
|
||||
GD.PushWarning("MagneticSkillComponent: Owner is not a Player/Node2D.");
|
||||
}
|
||||
|
||||
if (MagneticArea == null)
|
||||
@@ -96,34 +93,34 @@ public partial class MagneticSkillComponent : Node, ISkill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
|
||||
|
||||
if (Data.Level > 0 && Data.Upgrades.Count >= Data.Level)
|
||||
{
|
||||
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
|
||||
ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
public override void Activate()
|
||||
{
|
||||
if (MagneticArea == null)
|
||||
{
|
||||
GD.PushError("MagneticSkillComponent: MagneticArea is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MagneticArea.BodyEntered += OnBodyEntered;
|
||||
MagneticArea.AreaEntered += OnAreaEntered;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (MagneticArea == null) return;
|
||||
|
||||
|
||||
MagneticArea.BodyEntered -= OnBodyEntered;
|
||||
MagneticArea.AreaEntered -= OnAreaEntered;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
public override void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
foreach (var property in upgrade.Properties)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
@@ -10,7 +11,7 @@ namespace Mr.BrickAdventures.scripts.components;
|
||||
public partial class PlayerController : CharacterBody2D
|
||||
{
|
||||
[Export] private Node MovementAbilitiesContainer { get; set; }
|
||||
|
||||
|
||||
[ExportGroup("Movement Ability Scenes")]
|
||||
[Export] public PackedScene GroundMovementScene { get; set; }
|
||||
[Export] public PackedScene JumpMovementScene { get; set; }
|
||||
@@ -19,23 +20,23 @@ public partial class PlayerController : CharacterBody2D
|
||||
[Export] public PackedScene SpaceshipMovementScene { get; set; }
|
||||
[Export] public PackedScene WallJumpScene { get; set; }
|
||||
[Export] public PackedScene GridMovementScene { get; set; }
|
||||
|
||||
|
||||
[Signal] public delegate void JumpInitiatedEventHandler();
|
||||
[Signal] public delegate void MovementAbilitiesChangedEventHandler();
|
||||
|
||||
|
||||
public Vector2 LastDirection { get; private set; } = Vector2.Right;
|
||||
public Vector2 PreviousVelocity { get; private set; } = Vector2.Zero;
|
||||
|
||||
|
||||
private List<MovementAbility> _abilities = [];
|
||||
private PlayerInputHandler _inputHandler;
|
||||
|
||||
|
||||
public IReadOnlyList<MovementAbility> GetActiveAbilities() => _abilities;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var skillManager = GetNodeOrNull<SkillManager>("/root/SkillManager");
|
||||
var skillManager = GetNodeOrNull<SkillManager>(Constants.SkillManagerPath);
|
||||
skillManager?.RegisterPlayer(this);
|
||||
|
||||
|
||||
_inputHandler = GetNode<PlayerInputHandler>("PlayerInputHandler");
|
||||
foreach (var child in MovementAbilitiesContainer.GetChildren())
|
||||
{
|
||||
@@ -45,20 +46,20 @@ public partial class PlayerController : CharacterBody2D
|
||||
_abilities.Add(ability);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_ = ConnectJumpAndGravityAbilities();
|
||||
EmitSignalMovementAbilitiesChanged();
|
||||
}
|
||||
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
var velocity = Velocity;
|
||||
|
||||
|
||||
foreach (var ability in _abilities)
|
||||
{
|
||||
velocity = ability.ProcessMovement(velocity, delta);
|
||||
}
|
||||
|
||||
|
||||
if (_inputHandler.MoveDirection.X != 0)
|
||||
{
|
||||
LastDirection = new Vector2(_inputHandler.MoveDirection.X > 0 ? 1 : -1, 0);
|
||||
@@ -68,14 +69,14 @@ public partial class PlayerController : CharacterBody2D
|
||||
Velocity = velocity;
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
|
||||
public void AddAbility(MovementAbility ability)
|
||||
{
|
||||
MovementAbilitiesContainer.AddChild(ability);
|
||||
ability.Initialize(this);
|
||||
_abilities.Add(ability);
|
||||
}
|
||||
|
||||
|
||||
public void ClearMovementAbilities()
|
||||
{
|
||||
foreach (var ability in _abilities)
|
||||
@@ -84,7 +85,7 @@ public partial class PlayerController : CharacterBody2D
|
||||
}
|
||||
_abilities.Clear();
|
||||
}
|
||||
|
||||
|
||||
public void RemoveAbility<T>() where T : MovementAbility
|
||||
{
|
||||
for (var i = _abilities.Count - 1; i >= 0; i--)
|
||||
@@ -102,20 +103,20 @@ public partial class PlayerController : CharacterBody2D
|
||||
public void SetPlatformMovement()
|
||||
{
|
||||
ClearMovementAbilities();
|
||||
|
||||
|
||||
if (GroundMovementScene != null) AddAbility(GroundMovementScene.Instantiate<MovementAbility>());
|
||||
if (JumpMovementScene != null) AddAbility(JumpMovementScene.Instantiate<MovementAbility>());
|
||||
if (GravityScene != null) AddAbility(GravityScene.Instantiate<MovementAbility>());
|
||||
if (OneWayPlatformScene != null) AddAbility(OneWayPlatformScene.Instantiate<MovementAbility>());
|
||||
|
||||
|
||||
_ = ConnectJumpAndGravityAbilities();
|
||||
EmitSignalMovementAbilitiesChanged();
|
||||
}
|
||||
|
||||
|
||||
public void SetSpaceshipMovement()
|
||||
{
|
||||
ClearMovementAbilities();
|
||||
|
||||
|
||||
if (SpaceshipMovementScene != null) AddAbility(SpaceshipMovementScene.Instantiate<MovementAbility>());
|
||||
EmitSignalMovementAbilitiesChanged();
|
||||
}
|
||||
@@ -130,10 +131,10 @@ public partial class PlayerController : CharacterBody2D
|
||||
private async Task ConnectJumpAndGravityAbilities()
|
||||
{
|
||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
|
||||
|
||||
var jumpAbility = _abilities.OfType<VariableJumpAbility>().FirstOrDefault();
|
||||
var gravityAbility = _abilities.OfType<GravityAbility>().FirstOrDefault();
|
||||
|
||||
|
||||
if (jumpAbility != null && gravityAbility != null)
|
||||
{
|
||||
gravityAbility.AscendGravity = jumpAbility.AscendGravity;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
@@ -10,12 +11,12 @@ public partial class PlayerDeathComponent : Node2D
|
||||
[Export] public PackedScene DeathEffect { get; set; }
|
||||
[Export] public HealthComponent HealthComponent { get; set; }
|
||||
[Export] public Vector2 EffectScale { get; set; } = new Vector2(1.5f, 1.5f);
|
||||
|
||||
|
||||
private GameManager _gameManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
HealthComponent.Death += OnDeath;
|
||||
}
|
||||
|
||||
@@ -30,8 +31,8 @@ public partial class PlayerDeathComponent : Node2D
|
||||
effect.GlobalPosition = GlobalPosition;
|
||||
effect.Scale = EffectScale;
|
||||
}
|
||||
|
||||
_gameManager.RemoveLives(1);
|
||||
_gameManager.ResetCurrentSessionState();
|
||||
|
||||
// Lives are now decremented by LivesStateHandler via PlayerDied event
|
||||
// Session state is reset by LivesStateHandler as well
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class ScoreComponent : Node
|
||||
{
|
||||
private GameManager _gameManager;
|
||||
private const string CoinsGroupName = "coins";
|
||||
|
||||
public override async void _Ready()
|
||||
{
|
||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
if (_gameManager == null)
|
||||
{
|
||||
GD.PrintErr("GameManager not found in the scene tree.");
|
||||
return;
|
||||
}
|
||||
|
||||
var coins = GetTree().GetNodesInGroup(CoinsGroupName);
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
var c = coin.GetNodeOrNull<CollectableComponent>("CollectableComponent");
|
||||
if (c != null)
|
||||
{
|
||||
c.Collected += OnCollected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollected(float amount, CollectableType type, Node2D body)
|
||||
{
|
||||
if (type != CollectableType.Coin) return;
|
||||
|
||||
var coinAmount = (int)amount;
|
||||
var currentCoins = (int)_gameManager.CurrentSessionState["coins_collected"];
|
||||
_gameManager.CurrentSessionState["coins_collected"] = currentCoins + coinAmount;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://ccqb8kd5m0eh7
|
||||
29
scripts/components/SkillComponentBase.cs
Normal file
29
scripts/components/SkillComponentBase.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all skill components to reduce boilerplate.
|
||||
/// </summary>
|
||||
public abstract partial class SkillComponentBase : Node, ISkill
|
||||
{
|
||||
protected PlayerController Player { get; private set; }
|
||||
protected SkillData Data { get; private set; }
|
||||
|
||||
public virtual void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
Player = owner as PlayerController;
|
||||
Data = data;
|
||||
|
||||
if (Player == null)
|
||||
{
|
||||
GD.PrintErr($"{GetType().Name} must be a child of a PlayerController.");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Activate();
|
||||
public abstract void Deactivate();
|
||||
public virtual void ApplyUpgrade(SkillUpgrade upgrade) { }
|
||||
}
|
||||
1
scripts/components/SkillComponentBase.cs.uid
Normal file
1
scripts/components/SkillComponentBase.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dvuevrf5vr5jk
|
||||
@@ -1,8 +1,10 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
using Mr.BrickAdventures.scripts.State;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
@@ -18,8 +20,8 @@ public partial class SkillUnlockerComponent : Node
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
SkillManager = GetNode<SkillManager>("/root/SkillManager");
|
||||
_gameManager = GameManager.Instance;
|
||||
SkillManager = SkillManager.Instance;
|
||||
}
|
||||
|
||||
private bool HasEnoughCoins(int amount)
|
||||
@@ -34,11 +36,10 @@ public partial class SkillUnlockerComponent : Node
|
||||
if (!HasEnoughCoins(skill.Upgrades[0].Cost)) return false;
|
||||
|
||||
skill.Level = 1;
|
||||
skill.IsActive = true;
|
||||
_gameManager.RemoveCoins(skill.Upgrades[0].Cost);
|
||||
|
||||
var skillsUnlocked = (Array<SkillData>)_gameManager.CurrentSessionState["skills_unlocked"];
|
||||
skillsUnlocked.Add(skill);
|
||||
// Add to session state via GameStateStore
|
||||
GameStateStore.Instance?.UnlockSkillInSession(skill);
|
||||
SkillManager.AddSkill(skill);
|
||||
EmitSignalSkillUnlocked(skill);
|
||||
|
||||
|
||||
@@ -5,18 +5,19 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class XRayVisionSkillComponent : Node, ISkill
|
||||
public partial class XRayVisionSkillComponent : SkillComponentBase
|
||||
{
|
||||
[Export(PropertyHint.Layers2DRender)] public uint SecretLayer { get; set; }
|
||||
[Export] public float Duration { get; set; } = 5.0f;
|
||||
|
||||
|
||||
private Camera2D _camera;
|
||||
private Viewport _viewport;
|
||||
private uint _originalVisibilityLayer;
|
||||
private Timer _timer;
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
|
||||
public override void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
base.Initialize(owner, data);
|
||||
_viewport = GetViewport();
|
||||
_camera = GetViewport().GetCamera2D();
|
||||
_timer = new Timer { OneShot = true };
|
||||
@@ -24,16 +25,16 @@ public partial class XRayVisionSkillComponent : Node, ISkill
|
||||
_timer.Timeout += Deactivate;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
public override void Activate()
|
||||
{
|
||||
if (_camera == null) return;
|
||||
|
||||
|
||||
_originalVisibilityLayer = _camera.VisibilityLayer;
|
||||
_camera.VisibilityLayer |= SecretLayer;
|
||||
_timer.Start(Duration);
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (_camera != null)
|
||||
{
|
||||
@@ -41,7 +42,7 @@ public partial class XRayVisionSkillComponent : Node, ISkill
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
public override void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
if (upgrade.Properties.TryGetValue("duration", out var newDuration))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user