refactor (#6)

Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
2026-02-01 11:47:40 +00:00
parent dde3eaa52e
commit bfe951939d
71 changed files with 1583 additions and 661 deletions

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)
{

View File

@@ -1,5 +1,6 @@
using System;
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
@@ -35,7 +36,7 @@ public partial class CollectableComponent : Node
if (Owner.HasNode("FadeAwayComponent"))
_hasFadeAway = true;
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
_floatingTextManager = GetNode<FloatingTextManager>(Constants.FloatingTextManagerPath);
}
private async void OnArea2DBodyEntered(Node2D body)
@@ -53,12 +54,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;
}
}

View File

@@ -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>();
}
}

View File

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

View File

@@ -1,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));
}

View File

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

View File

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

View File

@@ -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");

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

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

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

@@ -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);

View File

@@ -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))
{