refactor #6

Merged
GKaszewski merged 9 commits from refactor into master 2026-02-01 11:47:42 +00:00
27 changed files with 212 additions and 179 deletions
Showing only changes of commit 288f0b1916 - Show all commits

View File

@@ -13,9 +13,9 @@ public partial class ConsoleManager : Node
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath); _achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath);
_skillManager = GetNode<SkillManager>(Constants.SkillManagerPath); _skillManager = SkillManager.Instance;
} }
private void AddCoinsCommand(int amount) private void AddCoinsCommand(int amount)

View File

@@ -14,6 +14,18 @@ public partial class FloatingTextManager : Node
[Export] public Color CoinColor { get; set; } = new Color("#ebd320"); // Gold [Export] public Color CoinColor { get; set; } = new Color("#ebd320"); // Gold
[Export] public Color MessageColor { get; set; } = new Color("#ffffff"); // White [Export] public Color MessageColor { get; set; } = new Color("#ffffff"); // White
public static FloatingTextManager Instance { get; private set; }
public override void _Ready()
{
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
public void ShowDamage(float amount, Vector2 position) public void ShowDamage(float amount, Vector2 position)
{ {
var text = Mathf.Round(amount * 100f).ToString(CultureInfo.InvariantCulture); var text = Mathf.Round(amount * 100f).ToString(CultureInfo.InvariantCulture);

View File

@@ -30,6 +30,8 @@ public partial class GameManager : Node
/// </summary> /// </summary>
private GameStateStore Store => GameStateStore.Instance; private GameStateStore Store => GameStateStore.Instance;
public static GameManager Instance { get; private set; }
public override void _EnterTree() public override void _EnterTree()
{ {
GetTree().NodeAdded += OnNodeAdded; GetTree().NodeAdded += OnNodeAdded;
@@ -38,6 +40,7 @@ public partial class GameManager : Node
public override void _ExitTree() public override void _ExitTree()
{ {
if (Instance == this) Instance = null;
GetTree().NodeAdded -= OnNodeAdded; GetTree().NodeAdded -= OnNodeAdded;
GetTree().NodeRemoved -= OnNodeRemoved; GetTree().NodeRemoved -= OnNodeRemoved;
_sceneNodes.Clear(); _sceneNodes.Clear();
@@ -45,6 +48,7 @@ public partial class GameManager : Node
public override void _Ready() public override void _Ready()
{ {
Instance = this;
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath); _speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
} }
@@ -140,11 +144,9 @@ public partial class GameManager : Node
{ {
if (Store == null) return new Array<SkillData>(); if (Store == null) return new Array<SkillData>();
var skills = Store.GetAllUnlockedSkills();
var result = new Array<SkillData>(); var result = new Array<SkillData>();
foreach (var s in Store.Player.UnlockedSkills) foreach (var s in skills) result.Add(s);
result.Add(s);
foreach (var s in Store.Session.SkillsUnlocked)
if (!result.Contains(s)) result.Add(s);
return result; return result;
} }

View File

@@ -1,4 +1,5 @@
using Godot; using Godot;
using System.Collections.Generic;
using Mr.BrickAdventures.scripts.Resources; using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State; using Mr.BrickAdventures.scripts.State;
@@ -163,6 +164,19 @@ public partial class GameStateStore : Node
Session.SkillsUnlocked.Clear(); Session.SkillsUnlocked.Clear();
} }
/// <summary>
/// Gets all unlocked skills from player persistence and current session.
/// </summary>
public List<SkillData> GetAllUnlockedSkills()
{
var result = new List<SkillData>(Player.UnlockedSkills);
foreach (var skill in Session.SkillsUnlocked)
{
if (!result.Contains(skill)) result.Add(skill);
}
return result;
}
#endregion #endregion
#region Reset Operations #region Reset Operations

View File

@@ -14,12 +14,24 @@ public partial class SaveSystem : Node
[Export] public string SavePath { get; set; } = "user://savegame.json"; [Export] public string SavePath { get; set; } = "user://savegame.json";
[Export] public int Version { get; set; } = 2; [Export] public int Version { get; set; } = 2;
public static SaveSystem Instance { get; private set; }
private static readonly JsonSerializerOptions JsonOptions = new() private static readonly JsonSerializerOptions JsonOptions = new()
{ {
WriteIndented = true, WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}; };
public override void _Ready()
{
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
public void SaveGame() public void SaveGame()
{ {
var store = GameStateStore.Instance; var store = GameStateStore.Instance;

View File

@@ -18,6 +18,8 @@ public partial class SkillManager : Node
public Dictionary ActiveComponents { get; private set; } = new(); public Dictionary ActiveComponents { get; private set; } = new();
public static SkillManager Instance { get; private set; }
[Signal] [Signal]
public delegate void ActiveThrowSkillChangedEventHandler(BrickThrowComponent throwComponent); public delegate void ActiveThrowSkillChangedEventHandler(BrickThrowComponent throwComponent);
[Signal] [Signal]
@@ -25,9 +27,15 @@ public partial class SkillManager : Node
public override void _Ready() public override void _Ready()
{ {
Instance = this;
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GetNode<GameManager>(Constants.GameManagerPath);
} }
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
/// <summary> /// <summary>
/// Called by the PlayerController from its _Ready method to register itself with the manager. /// Called by the PlayerController from its _Ready method to register itself with the manager.
/// </summary> /// </summary>
@@ -143,7 +151,6 @@ public partial class SkillManager : Node
{ {
if (s.Name == skillName) if (s.Name == skillName)
{ {
s.IsActive = false;
break; break;
} }
} }
@@ -188,12 +195,16 @@ public partial class SkillManager : Node
return null; return null;
} }
public bool IsSkillActive(SkillData skill)
{
return skill != null && ActiveComponents.ContainsKey(skill.Name);
}
public void ActivateSkill(SkillData skill) public void ActivateSkill(SkillData skill)
{ {
if (!ActiveComponents.ContainsKey(skill.Name)) if (!ActiveComponents.ContainsKey(skill.Name))
{ {
AddSkill(skill); AddSkill(skill);
skill.IsActive = true;
} }
} }
@@ -202,7 +213,6 @@ public partial class SkillManager : Node
if (ActiveComponents.ContainsKey(skill.Name)) if (ActiveComponents.ContainsKey(skill.Name))
{ {
RemoveSkill(skill.Name); RemoveSkill(skill.Name);
skill.IsActive = false;
} }
} }

View File

@@ -14,7 +14,7 @@ public partial class SkillCollectHandler : Node
public override void _Ready() public override void _Ready()
{ {
_skillManager = GetNode<SkillManager>(Constants.SkillManagerPath); _skillManager = SkillManager.Instance;
EventBus.Instance.SkillCollected += OnSkillCollected; EventBus.Instance.SkillCollected += OnSkillCollected;
} }
@@ -34,7 +34,6 @@ public partial class SkillCollectHandler : Node
GameStateStore.Instance?.UnlockSkillInSession(skill); GameStateStore.Instance?.UnlockSkillInSession(skill);
// Immediately activate the skill for the player // Immediately activate the skill for the player
skill.IsActive = true;
skill.Level = 1; skill.Level = 1;
_skillManager?.AddSkill(skill); _skillManager?.AddSkill(skill);

View File

@@ -10,7 +10,6 @@ public partial class SkillData : Resource
[Export] public string Name { get; set; } = "New Skill"; [Export] public string Name { get; set; } = "New Skill";
[Export] public string Description { get; set; } = "New Skill"; [Export] public string Description { get; set; } = "New Skill";
[Export] public Texture2D Icon { get; set; } [Export] public Texture2D Icon { get; set; }
[Export] public bool IsActive { get; set; } = false;
[Export] public int Level { get; set; } = 1; [Export] public int Level { get; set; } = 1;
[Export] public SkillType Type { get; set; } = SkillType.Throw; [Export] public SkillType Type { get; set; } = SkillType.Throw;
[Export] public PackedScene Node { get; set; } [Export] public PackedScene Node { get; set; }

View File

@@ -20,7 +20,7 @@ public partial class DeathScreen : Control
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
// Subscribe to lives changed event for reactive updates // Subscribe to lives changed event for reactive updates
EventBus.Instance.LivesChanged += OnLivesChanged; EventBus.Instance.LivesChanged += OnLivesChanged;

View File

@@ -15,7 +15,7 @@ public partial class GameOverScreen : Control
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
RestartButton.Pressed += OnRestartClicked; RestartButton.Pressed += OnRestartClicked;
MainMenuButton.Pressed += OnMainMenuClicked; MainMenuButton.Pressed += OnMainMenuClicked;
} }

View File

@@ -16,7 +16,7 @@ public partial class Hud : Control
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
} }
public override void _Process(double delta) public override void _Process(double delta)

View File

@@ -22,8 +22,8 @@ public partial class MainMenu : Control
public override void _Ready() public override void _Ready()
{ {
_saveSystem = GetNode<SaveSystem>(Constants.SaveSystemPath); _saveSystem = SaveSystem.Instance;
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_uiManager = GetNode<UIManager>(Constants.UIManagerPath); _uiManager = GetNode<UIManager>(Constants.UIManagerPath);
NewGameButton.Pressed += OnNewGamePressed; NewGameButton.Pressed += OnNewGamePressed;

View File

@@ -26,9 +26,8 @@ public partial class Marketplace : Control
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_skillManager = GetNode<SkillManager>(Constants.SkillManagerPath); _skillManager = SkillManager.Instance;
_skillManager.SkillRemoved += OnSkillRemoved;
Skills = _skillManager.AvailableSkills; Skills = _skillManager.AvailableSkills;
@@ -42,11 +41,16 @@ public partial class Marketplace : Control
foreach (var skill in unlockedSkills) CreateSkillButton(skill); foreach (var skill in unlockedSkills) CreateSkillButton(skill);
SkillUnlockerComponent.SkillUnlocked += OnSkillUnlocked; SkillUnlockerComponent.SkillUnlocked += OnSkillUnlocked;
EventBus.Instance.SkillCollected += OnGlobalSkillCollected;
} }
public override void _ExitTree() public override void _ExitTree()
{ {
SkillUnlockerComponent.SkillUnlocked -= OnSkillUnlocked; SkillUnlockerComponent.SkillUnlocked -= OnSkillUnlocked;
if (EventBus.Instance != null)
{
EventBus.Instance.SkillCollected -= OnGlobalSkillCollected;
}
} }
public override void _Input(InputEvent @event) public override void _Input(InputEvent @event)
@@ -81,7 +85,7 @@ public partial class Marketplace : Control
foreach (var btn in _skillButtons) foreach (var btn in _skillButtons)
{ {
if (btn.Data.IsActive) btn.Activate(); if (_skillManager.IsSkillActive(btn.Data)) btn.Activate();
else btn.Deactivate(); else btn.Deactivate();
} }
} }
@@ -118,7 +122,7 @@ public partial class Marketplace : Control
if (skill.Level < skill.MaxLevel) if (skill.Level < skill.MaxLevel)
{ {
SkillUnlockerComponent.TryUpgradeSkill(skill); SkillUnlockerComponent.TryUpgradeSkill(skill);
if (!skill.IsActive) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill); if (!SkillUnlockerComponent.SkillManager.IsSkillActive(skill)) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill);
} }
else else
{ {
@@ -137,28 +141,15 @@ public partial class Marketplace : Control
foreach (var btn in _skillButtons) foreach (var btn in _skillButtons)
{ {
if (btn.Data.IsActive) if (SkillUnlockerComponent.SkillManager.IsSkillActive(btn.Data))
btn.Activate(); btn.Activate();
else else
btn.Deactivate(); btn.Deactivate();
} }
} }
private void OnSkillRemoved(SkillData skill) private void OnGlobalSkillCollected(SkillData skill, Vector2 position)
{ {
SkillButton buttonToRemove = null; OnSkillUnlocked(skill);
foreach (var button in _skillButtons)
{
if (button.Data == skill)
{
buttonToRemove = button;
break;
}
}
if (buttonToRemove != null)
{
_skillButtons.Remove(buttonToRemove);
buttonToRemove.QueueFree();
}
} }
} }

View File

@@ -19,7 +19,7 @@ public partial class MarketplaceButton : Button
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
var player = _gameManager.Player; var player = _gameManager.Player;
if (player == null) return; if (player == null) return;
@@ -29,7 +29,7 @@ public partial class MarketplaceButton : Button
_skillUnlockerComponent.SkillUnlocked += OnSkillStateChanged; _skillUnlockerComponent.SkillUnlocked += OnSkillStateChanged;
} }
_skillManager = GetNode<SkillManager>(Constants.SkillManagerPath); _skillManager = SkillManager.Instance;
_skillManager.SkillRemoved += OnSkillStateChanged; _skillManager.SkillRemoved += OnSkillStateChanged;
UpdateButtonState(); UpdateButtonState();

View File

@@ -19,7 +19,7 @@ public partial class PauseMenu : Control
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_uiManager = GetNode<UIManager>(Constants.UIManagerPath); _uiManager = GetNode<UIManager>(Constants.UIManagerPath);
ResumeButton.Pressed += OnResumePressed; ResumeButton.Pressed += OnResumePressed;

View File

@@ -5,26 +5,27 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class BrickArmorSkillComponent : Node, ISkill public partial class BrickArmorSkillComponent : SkillComponentBase
{ {
private HealthComponent _healthComponent; private HealthComponent _healthComponent;
private SkillData _skillData;
private float _armorBonus = 0; 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; base.Initialize(owner, data);
_healthComponent = player.GetNode<HealthComponent>("HealthComponent"); if (Player != null)
_skillData = data; {
_healthComponent = Player.GetNode<HealthComponent>("HealthComponent");
}
} }
public void Activate() public override void Activate()
{ {
if (_healthComponent == null || _skillData == null) return; if (_healthComponent == null || Data == null) return;
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]); ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
} }
public void Deactivate() public override void Deactivate()
{ {
if (_healthComponent == null) return; if (_healthComponent == null) return;
_healthComponent.MaxHealth -= _armorBonus; _healthComponent.MaxHealth -= _armorBonus;
@@ -35,7 +36,7 @@ public partial class BrickArmorSkillComponent : Node, ISkill
_armorBonus = 0; _armorBonus = 0;
} }
public void ApplyUpgrade(SkillUpgrade upgrade) public override void ApplyUpgrade(SkillUpgrade upgrade)
{ {
if (_healthComponent == null || upgrade == null) return; if (_healthComponent == null || upgrade == null) return;

View File

@@ -7,41 +7,33 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class BrickShieldSkillComponent : Node, ISkill public partial class BrickShieldSkillComponent : SkillComponentBase
{ {
[Export] public PackedScene ShieldScene { get; set; } [Export] public PackedScene ShieldScene { get; set; }
private PlayerController _player;
private Node2D _shieldInstance; private Node2D _shieldInstance;
private SkillData _skillData;
private GameManager _gameManager; private GameManager _gameManager;
private SkillManager _skillManager; private SkillManager _skillManager;
private HealthComponent _shieldHealth; private HealthComponent _shieldHealth;
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_skillManager = GetNode<SkillManager>(Constants.SkillManagerPath); _skillManager = SkillManager.Instance;
} }
public void Initialize(Node owner, SkillData data) public override void Activate()
{ {
_player = owner as PlayerController; if (Player == null || ShieldScene == null || _shieldInstance != null) return;
_skillData = data;
}
public void Activate()
{
if (_player == null || ShieldScene == null || _shieldInstance != null) return;
_shieldInstance = ShieldScene.Instantiate<Node2D>(); _shieldInstance = ShieldScene.Instantiate<Node2D>();
_player.AddChild(_shieldInstance); Player.AddChild(_shieldInstance);
_shieldInstance.Position = Vector2.Zero; _shieldInstance.Position = Vector2.Zero;
_shieldInstance.TreeExiting += OnShieldDestroyed; _shieldInstance.TreeExiting += OnShieldDestroyed;
_shieldHealth = _shieldInstance.GetNode<HealthComponent>("HealthComponent"); _shieldHealth = _shieldInstance.GetNode<HealthComponent>("HealthComponent");
} }
public void Deactivate() public override void Deactivate()
{ {
if (_shieldInstance != null && IsInstanceValid(_shieldInstance)) if (_shieldInstance != null && IsInstanceValid(_shieldInstance))
{ {
@@ -51,7 +43,7 @@ public partial class BrickShieldSkillComponent : Node, ISkill
_shieldInstance = null; _shieldInstance = null;
} }
public void ApplyUpgrade(SkillUpgrade upgrade) public override void ApplyUpgrade(SkillUpgrade upgrade)
{ {
upgrade.Properties.TryGetValue("shield_health", out var newHealth); upgrade.Properties.TryGetValue("shield_health", out var newHealth);
if (_shieldHealth != null) if (_shieldHealth != null)
@@ -63,10 +55,10 @@ public partial class BrickShieldSkillComponent : Node, ISkill
private void OnShieldDestroyed() private void OnShieldDestroyed()
{ {
if (_gameManager != null && _skillData != null && _skillManager != null) if (_gameManager != null && Data != null && _skillManager != null)
{ {
_gameManager.RemoveSkill(_skillData.Name); _gameManager.RemoveSkill(Data.Name);
_skillManager.RemoveSkill(_skillData.Name); _skillManager.RemoveSkill(Data.Name);
} }
_shieldInstance = null; _shieldInstance = null;
} }

View File

@@ -5,16 +5,14 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class BrickThrowComponent : Node, ISkill public partial class BrickThrowComponent : SkillComponentBase
{ {
[Export] public PackedScene BrickScene { get; set; } [Export] public PackedScene BrickScene { get; set; }
[Export] public float FireRate { get; set; } = 1.0f; [Export] public float FireRate { get; set; } = 1.0f;
[Export] public PlayerController PlayerController { get; set; }
[Export] public ThrowInputResource ThrowInputBehavior { get; set; } [Export] public ThrowInputResource ThrowInputBehavior { get; set; }
private bool _canThrow = true; private bool _canThrow = true;
private Timer _timer; private Timer _timer;
private SkillData _skillData;
public override void _Ready() public override void _Ready()
{ {
@@ -59,7 +57,7 @@ public partial class BrickThrowComponent : Node, ISkill
private void ThrowBrick(float powerMultiplier = 1f) private void ThrowBrick(float powerMultiplier = 1f)
{ {
if (!_canThrow || PlayerController == null || BrickScene == null) if (!_canThrow || Player == null || BrickScene == null)
return; return;
var instance = BrickScene.Instantiate<Node2D>(); var instance = BrickScene.Instantiate<Node2D>();
@@ -69,9 +67,9 @@ public partial class BrickThrowComponent : Node, ISkill
{ {
var @params = new ProjectileInitParams() var @params = new ProjectileInitParams()
{ {
Position = PlayerController.GlobalPosition, Position = Player.GlobalPosition,
Rotation = PlayerController.Rotation, Rotation = Player.Rotation,
Direction = PlayerController.LastDirection, Direction = Player.LastDirection,
PowerMultiplier = powerMultiplier, PowerMultiplier = powerMultiplier,
}; };
@@ -83,36 +81,30 @@ public partial class BrickThrowComponent : Node, ISkill
_timer.Start(); _timer.Start();
} }
public void Initialize(Node owner, SkillData data) public override void Initialize(Node owner, SkillData data)
{ {
PlayerController = owner as PlayerController; base.Initialize(owner, data);
_skillData = data;
ThrowInputBehavior = (ThrowInputResource)ThrowInputBehavior?.Duplicate(); ThrowInputBehavior = (ThrowInputResource)ThrowInputBehavior?.Duplicate();
if (PlayerController == null) if (Data.Level > 0 && Data.Upgrades.Count >= Data.Level)
{ {
GD.PushError("BrickThrowComponent: Owner is not a PlayerController."); ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
}
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
{
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
} }
} }
public void Activate() public override void Activate()
{ {
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick; if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
SetProcessInput(true); SetProcessInput(true);
} }
public void Deactivate() public override void Deactivate()
{ {
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick; if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick;
} }
public void ApplyUpgrade(SkillUpgrade upgrade) public override void ApplyUpgrade(SkillUpgrade upgrade)
{ {
foreach (var property in upgrade.Properties) foreach (var property in upgrade.Properties)
{ {

View File

@@ -6,40 +6,25 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class DoubleJumpSkillComponent : Node, ISkill public partial class DoubleJumpSkillComponent : SkillComponentBase
{ {
[Export] private PackedScene _doubleJumpAbilityScene; [Export] private PackedScene _doubleJumpAbilityScene;
private PlayerController _playerController;
public void Initialize(Node owner, SkillData data) public override void Activate()
{ {
_playerController = owner as PlayerController; if (Player == null) return;
if (_playerController == null)
{
GD.PrintErr("DoubleJumpSkillComponent must be a child of a PlayerController.");
}
}
public void Activate() var hasAbility = Player.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
{
if (_playerController == null) return;
var hasAbility = _playerController.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
if (!hasAbility) if (!hasAbility)
{ {
var abilityInstance = _doubleJumpAbilityScene.Instantiate<DoubleJumpAbility>(); var abilityInstance = _doubleJumpAbilityScene.Instantiate<DoubleJumpAbility>();
_playerController.AddAbility(abilityInstance); Player.AddAbility(abilityInstance);
} }
} }
public void Deactivate() public override void Deactivate()
{ {
_playerController?.RemoveAbility<DoubleJumpAbility>(); Player?.RemoveAbility<DoubleJumpAbility>();
}
public void ApplyUpgrade(SkillUpgrade upgrade)
{
} }
} }

View File

@@ -22,7 +22,7 @@ public partial class ExitDoorComponent : Area2D, IUnlockable
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
_achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath); _achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath);
BodyEntered += OnExitAreaBodyEntered; BodyEntered += OnExitAreaBodyEntered;

View File

@@ -5,62 +5,59 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class GroundPoundSkillComponent : Node, ISkill public partial class GroundPoundSkillComponent : SkillComponentBase
{ {
[Export] public float PoundForce { get; set; } = 1200f; [Export] public float PoundForce { get; set; } = 1200f;
[Export] public PackedScene ShockwaveScene { get; set; } [Export] public PackedScene ShockwaveScene { get; set; }
private PlayerController _player;
private PlayerInputHandler _input; private PlayerInputHandler _input;
private bool _isPounding = false; private bool _isPounding = false;
public void Initialize(Node owner, SkillData data) public override void Initialize(Node owner, SkillData data)
{ {
_player = owner as PlayerController; base.Initialize(owner, data);
if (_player != null) if (Player != null)
{ {
_input = _player.GetNode<PlayerInputHandler>("PlayerInputHandler"); _input = Player.GetNode<PlayerInputHandler>("PlayerInputHandler");
} }
} }
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
if (_player == null || _input == null) if (Player == null || _input == null)
{ {
return; return;
} }
// Check if we just landed from a ground pound to create the shockwave. // Check if we just landed from a ground pound to create the shockwave.
if (_isPounding && _player.IsOnFloor()) if (_isPounding && Player.IsOnFloor())
{ {
_isPounding = false; _isPounding = false;
if (ShockwaveScene != null) if (ShockwaveScene != null)
{ {
var shockwave = ShockwaveScene.Instantiate<Node2D>(); var shockwave = ShockwaveScene.Instantiate<Node2D>();
_player.GetParent()?.AddChild(shockwave); Player.GetParent()?.AddChild(shockwave);
shockwave.GlobalPosition = _player.GlobalPosition; shockwave.GlobalPosition = Player.GlobalPosition;
} }
} }
// Check to initiate a ground pound. The player must be in the air. // 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. // Apply a strong downward force, zeroing out horizontal movement.
_player.Velocity = new Vector2(0, PoundForce); Player.Velocity = new Vector2(0, PoundForce);
_isPounding = true; _isPounding = true;
} }
} }
public void Activate() public override void Activate()
{ {
SetPhysicsProcess(true); SetPhysicsProcess(true);
} }
public void Deactivate() public override void Deactivate()
{ {
SetPhysicsProcess(false); SetPhysicsProcess(false);
_isPounding = false; _isPounding = false;
} }
public void ApplyUpgrade(SkillUpgrade upgrade) { }
} }

View File

@@ -7,14 +7,12 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class MagneticSkillComponent : Node, ISkill public partial class MagneticSkillComponent : SkillComponentBase
{ {
[Export] public Area2D MagneticArea { get; set; } [Export] public Area2D MagneticArea { get; set; }
[Export] public float MagneticMoveDuration { get; set; } = 1.25f; [Export] public float MagneticMoveDuration { get; set; } = 1.25f;
private Array<Node2D> _collectablesToPickUp = []; private Array<Node2D> _collectablesToPickUp = [];
private Node2D _owner;
private SkillData _skillData;
public override void _Process(double delta) public override void _Process(double delta)
{ {
@@ -65,22 +63,21 @@ public partial class MagneticSkillComponent : Node, ISkill
private void MoveCollectableToOwner(Node2D collectable) private void MoveCollectableToOwner(Node2D collectable)
{ {
if (!IsInstanceValid(collectable) || !IsInstanceValid(_owner)) return; if (!IsInstanceValid(collectable) || !IsInstanceValid(Player)) return;
var direction = (_owner.GlobalPosition - collectable.GlobalPosition).Normalized(); var direction = (Player.GlobalPosition - collectable.GlobalPosition).Normalized();
var speed = direction.Length() / MagneticMoveDuration; var speed = direction.Length() / MagneticMoveDuration;
collectable.GlobalPosition += direction.Normalized() * speed; collectable.GlobalPosition += direction.Normalized() * speed;
} }
public void Initialize(Node owner, SkillData data) public override void Initialize(Node owner, SkillData data)
{ {
_owner = owner as Node2D; base.Initialize(owner, data);
_skillData = data;
if (_owner == null) if (Player == null)
{ {
GD.PushWarning("MagneticSkillComponent: Owner is not a Node2D."); GD.PushWarning("MagneticSkillComponent: Owner is not a Player/Node2D.");
} }
if (MagneticArea == null) if (MagneticArea == null)
@@ -97,13 +94,13 @@ 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) if (MagneticArea == null)
{ {
@@ -115,7 +112,7 @@ public partial class MagneticSkillComponent : Node, ISkill
MagneticArea.AreaEntered += OnAreaEntered; MagneticArea.AreaEntered += OnAreaEntered;
} }
public void Deactivate() public override void Deactivate()
{ {
if (MagneticArea == null) return; if (MagneticArea == null) return;
@@ -123,7 +120,7 @@ public partial class MagneticSkillComponent : Node, ISkill
MagneticArea.AreaEntered -= OnAreaEntered; MagneticArea.AreaEntered -= OnAreaEntered;
} }
public void ApplyUpgrade(SkillUpgrade upgrade) public override void ApplyUpgrade(SkillUpgrade upgrade)
{ {
foreach (var property in upgrade.Properties) foreach (var property in upgrade.Properties)
{ {

View File

@@ -16,7 +16,7 @@ public partial class PlayerDeathComponent : Node2D
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
HealthComponent.Death += OnDeath; HealthComponent.Death += OnDeath;
} }

View 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) { }
}

View File

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

View File

@@ -20,8 +20,8 @@ public partial class SkillUnlockerComponent : Node
public override void _Ready() public override void _Ready()
{ {
_gameManager = GetNode<GameManager>(Constants.GameManagerPath); _gameManager = GameManager.Instance;
SkillManager = GetNode<SkillManager>(Constants.SkillManagerPath); SkillManager = SkillManager.Instance;
} }
private bool HasEnoughCoins(int amount) private bool HasEnoughCoins(int amount)
@@ -36,7 +36,6 @@ public partial class SkillUnlockerComponent : Node
if (!HasEnoughCoins(skill.Upgrades[0].Cost)) return false; if (!HasEnoughCoins(skill.Upgrades[0].Cost)) return false;
skill.Level = 1; skill.Level = 1;
skill.IsActive = true;
_gameManager.RemoveCoins(skill.Upgrades[0].Cost); _gameManager.RemoveCoins(skill.Upgrades[0].Cost);
// Add to session state via GameStateStore // Add to session state via GameStateStore

View File

@@ -5,7 +5,7 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components; namespace Mr.BrickAdventures.scripts.components;
[GlobalClass] [GlobalClass]
public partial class XRayVisionSkillComponent : Node, ISkill public partial class XRayVisionSkillComponent : SkillComponentBase
{ {
[Export(PropertyHint.Layers2DRender)] public uint SecretLayer { get; set; } [Export(PropertyHint.Layers2DRender)] public uint SecretLayer { get; set; }
[Export] public float Duration { get; set; } = 5.0f; [Export] public float Duration { get; set; } = 5.0f;
@@ -15,8 +15,9 @@ public partial class XRayVisionSkillComponent : Node, ISkill
private uint _originalVisibilityLayer; private uint _originalVisibilityLayer;
private Timer _timer; private Timer _timer;
public void Initialize(Node owner, SkillData data) public override void Initialize(Node owner, SkillData data)
{ {
base.Initialize(owner, data);
_viewport = GetViewport(); _viewport = GetViewport();
_camera = GetViewport().GetCamera2D(); _camera = GetViewport().GetCamera2D();
_timer = new Timer { OneShot = true }; _timer = new Timer { OneShot = true };
@@ -24,7 +25,7 @@ public partial class XRayVisionSkillComponent : Node, ISkill
_timer.Timeout += Deactivate; _timer.Timeout += Deactivate;
} }
public void Activate() public override void Activate()
{ {
if (_camera == null) return; if (_camera == null) return;
@@ -33,7 +34,7 @@ public partial class XRayVisionSkillComponent : Node, ISkill
_timer.Start(Duration); _timer.Start(Duration);
} }
public void Deactivate() public override void Deactivate()
{ {
if (_camera != null) 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)) if (upgrade.Properties.TryGetValue("duration", out var newDuration))
{ {