- Fix double-execution bug in LevelStateHandler (coins/skills were committed twice per level) - Fix DamageComponent to track multiple targets via HashSet instead of single node - Fix HealthComponent: update health immediately, decouple from PlayerController via signal wiring in PlayerController - Remove dead loop in SkillManager.RemoveSkill; simplify O(n²) throw skill loop - Replace GetNode<T>(path) with .Instance across managers; add Instance to StatisticsManager/SpeedRunManager - GameManager.GetPlayer() now uses EventBus.PlayerSpawned instead of scanning all scene nodes - UIManager.UiStack: remove [Export], use private List<Control> - PlayerState: extract DefaultLives constant, simplify CreateDefault()
155 lines
5.0 KiB
C#
155 lines
5.0 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Godot;
|
|
using Mr.BrickAdventures.Autoloads;
|
|
|
|
namespace Mr.BrickAdventures.scripts.components;
|
|
|
|
[GlobalClass]
|
|
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; }
|
|
[Export] public PackedScene GravityScene { get; set; }
|
|
[Export] public PackedScene OneWayPlatformScene { get; set; }
|
|
[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()
|
|
{
|
|
SkillManager.Instance?.RegisterPlayer(this);
|
|
|
|
// Wire HealthComponent signals to global EventBus events
|
|
var health = GetNodeOrNull<HealthComponent>("HealthComponent");
|
|
if (health != null)
|
|
{
|
|
health.Death += () => EventBus.EmitPlayerDied(GlobalPosition);
|
|
health.HealthChanged += (delta, total) =>
|
|
{
|
|
if (delta < 0f) EventBus.EmitPlayerDamaged(Mathf.Abs(delta), total, GlobalPosition);
|
|
else EventBus.EmitPlayerHealed(delta, total, GlobalPosition);
|
|
};
|
|
}
|
|
|
|
_inputHandler = GetNode<PlayerInputHandler>("PlayerInputHandler");
|
|
foreach (var child in MovementAbilitiesContainer.GetChildren())
|
|
{
|
|
if (child is MovementAbility ability)
|
|
{
|
|
ability.Initialize(this);
|
|
_abilities.Add(ability);
|
|
}
|
|
}
|
|
|
|
_ = ConnectJumpAndGravityAbilities();
|
|
EmitSignalMovementAbilitiesChanged();
|
|
EventBus.EmitPlayerSpawned(this);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
PreviousVelocity = Velocity;
|
|
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)
|
|
{
|
|
ability.QueueFree();
|
|
}
|
|
_abilities.Clear();
|
|
}
|
|
|
|
public void RemoveAbility<T>() where T : MovementAbility
|
|
{
|
|
for (var i = _abilities.Count - 1; i >= 0; i--)
|
|
{
|
|
if (_abilities[i] is T)
|
|
{
|
|
var ability = _abilities[i];
|
|
_abilities.RemoveAt(i);
|
|
ability.QueueFree();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
public void SetGridMovement()
|
|
{
|
|
ClearMovementAbilities();
|
|
if (GridMovementScene != null) AddAbility(GridMovementScene.Instantiate<MovementAbility>());
|
|
EmitSignalMovementAbilitiesChanged();
|
|
}
|
|
|
|
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;
|
|
gravityAbility.DescendGravity = jumpAbility.DescendGravity;
|
|
}
|
|
}
|
|
} |