using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Godot; 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; } [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 _abilities = []; private PlayerInputHandler _inputHandler; public IReadOnlyList GetActiveAbilities() => _abilities; public override void _Ready() { _inputHandler = GetNode("PlayerInputHandler"); foreach (var child in MovementAbilitiesContainer.GetChildren()) { if (child is MovementAbility ability) { ability.Initialize(this); _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); } PreviousVelocity = Velocity; Velocity = velocity; MoveAndSlide(); } public void AddAbility(MovementAbility ability) { MovementAbilitiesContainer.AddChild(ability); ability.Initialize(this); _abilities.Add(ability); } private void ClearMovementAbilities() { foreach (var ability in _abilities) { ability.QueueFree(); } _abilities.Clear(); } public void RemoveAbility() 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()); if (JumpMovementScene != null) AddAbility(JumpMovementScene.Instantiate()); if (GravityScene != null) AddAbility(GravityScene.Instantiate()); if (OneWayPlatformScene != null) AddAbility(OneWayPlatformScene.Instantiate()); _ = ConnectJumpAndGravityAbilities(); EmitSignalMovementAbilitiesChanged(); } public void SetSpaceshipMovement() { ClearMovementAbilities(); if (SpaceshipMovementScene != null) AddAbility(SpaceshipMovementScene.Instantiate()); EmitSignalMovementAbilitiesChanged(); } private async Task ConnectJumpAndGravityAbilities() { await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); var jumpAbility = _abilities.OfType().FirstOrDefault(); var gravityAbility = _abilities.OfType().FirstOrDefault(); if (jumpAbility != null && gravityAbility != null) { gravityAbility.AscendGravity = jumpAbility.AscendGravity; gravityAbility.DescendGravity = jumpAbility.DescendGravity; } } }