Add new components: CannotStompComponent, SkillUnlockedComponent, SpaceshipEnterComponent, SpaceshipExitComponent, SpinComponent, StompDamageComponent, StraightMotionComponent, TerrainHitFx, TooltipComponent, TrailComponent, and UnlockOnRequirementComponent

This commit is contained in:
2025-08-12 12:19:18 +02:00
parent ef4d128869
commit dfa8a17ba1
13 changed files with 332 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CannotStompComponent : Node
{
}

View File

@@ -1,9 +1,10 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.interfaces;
namespace Mr.BrickAdventures.scripts.components;
public partial class ExitDoorComponent : Node
public partial class ExitDoorComponent : Node, IUnlockable
{
[Export] public bool Locked { get; set; } = true;
[Export] public Area2D ExitArea { get; set; }
@@ -33,7 +34,7 @@ public partial class ExitDoorComponent : Node
throw new System.NotImplementedException();
}
private void Unlock()
public void Unlock()
{
Locked = false;
if (DoorSprite != null)

View File

@@ -0,0 +1,70 @@
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class SkillUnlockedComponent : Node
{
[Export] public SkillManager SkillManager { get; set; }
[Signal]
public delegate void SkillUnlockedEventHandler(SkillData skill);
private GameManager _gameManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
}
private bool HasEnoughCoins(int amount)
{
return _gameManager != null && _gameManager.GetCoins() >= amount;
}
public bool TryUnlockSkill(SkillData skill)
{
if (_gameManager == null) return false;
if (_gameManager.IsSkillUnlocked(skill)) return false;
if (!HasEnoughCoins(skill.Cost)) return false;
skill.Level = 1;
skill.IsActive = true;
_gameManager.RemoveCoins(skill.Cost);
var skillsUnlocked = (Array<SkillData>)_gameManager.CurrentSessionState["skills_unlocked"];
skillsUnlocked.Add(skill);
SkillManager.AddSkill(skill);
EmitSignalSkillUnlocked(skill);
return true;
}
public void UnlockAllSkills()
{
var availableSkills = SkillManager.AvailableSkills;
foreach (var skill in availableSkills)
{
EmitSignalSkillUnlocked(skill);
}
_gameManager.UnlockSkills(availableSkills);
SkillManager.ApplyUnlockedSkills();
}
public bool TryUpgradeSkill(SkillData skill)
{
if (_gameManager == null) return false;
if (!_gameManager.IsSkillUnlocked(skill)) return false;
if (!HasEnoughCoins(skill.Cost)) return false;
if (skill.Level >= skill.MaxLevel) return false;
_gameManager.RemoveCoins(skill.Cost);
skill.Level++;
EmitSignalSkillUnlocked(skill);
return true;
}
}

View File

@@ -0,0 +1,21 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class SpaceshipEnterComponent : Node
{
[Export] public Area2D Area { get; set; }
[Signal] public delegate void SpaceshipEnteredEventHandler();
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
if (body is not PlayerController) return;
EmitSignalSpaceshipEntered();
Owner.QueueFree();
}
}

View File

@@ -0,0 +1,20 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class SpaceshipExitComponent : Node
{
[Export] public Area2D Area { get; set; }
[Signal] public delegate void SpaceshipExitEventHandler();
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
if (body is not PlayerController) return;
EmitSignalSpaceshipExit();
}
}

View File

@@ -0,0 +1,23 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class SpinComponent : Node
{
[Export] public float SpinSpeed { get; set; } = 8f;
[Export] public Vector2 SpinDirection { get; set; } = Vector2.Right;
public override void _Process(double delta)
{
Spin((float)delta);
}
private void Spin(float delta)
{
var rotationSpeed = SpinSpeed * SpinDirection.X * delta;
if (Owner is Node2D root)
{
root.Rotation += rotationSpeed;
}
}
}

View File

@@ -0,0 +1,43 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class StompDamageComponent : Node
{
[Export] public float Damage { get; set; } = 0.25f;
[Export] public Area2D Area { get; set; }
[Export] public PlayerController Root { get; set; }
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private async void OnBodyEntered(Node2D body)
{
var health = body.GetNodeOrNull<HealthComponent>("HealthComponent");
if (health == null) return;
var cannotStompComponent = body.GetNodeOrNull<CannotStompComponent>("CannotStompComponent");
if (cannotStompComponent != null) return;
if (!(Root.GlobalPosition.Y < body.GlobalPosition.Y)) return;
var velocity = Root.CurrentMovement.PreviousVelocity;
if (!(velocity.Y > 0f)) return;
DealDamage(health);
var damageComponent = body.GetNodeOrNull<DamageComponent>("DamageComponent");
if (damageComponent == null) return;
damageComponent.SetProcess(false);
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
damageComponent.SetProcess(true);
}
private void DealDamage(HealthComponent target)
{
target.DecreaseHealth(Damage);
}
}

View File

@@ -0,0 +1,19 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class StraightMotionComponent : Node
{
[Export] public LaunchComponent LaunchComponent { get; set; }
public override void _PhysicsProcess(double delta)
{
var root = Owner as Node2D;
if (root == null || LaunchComponent == null)
{
return;
}
root.Position += LaunchComponent.GetInitialVelocity() * (float)delta;
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class TerrainHitFx : Node
{
private List<GpuParticles2D> _gpuParticles = [];
public override void _Ready()
{
if (Owner is GpuParticles2D gpuParticle) _gpuParticles.Add(gpuParticle);
foreach (var child in GetChildren())
{
if (child is GpuParticles2D p)
{
_gpuParticles.Add(p);
}
}
}
public void TriggerFx()
{
foreach (var fx in _gpuParticles.Where(fx => fx != null))
{
fx.Restart();
fx.Emitting = true;
}
}
}

View File

@@ -0,0 +1,39 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class TooltipComponent : Node
{
[Export] public Area2D Area { get; set; }
[Export] public Control UiRoot { get; set; }
[Export] public string Text { get; set; } = string.Empty;
[Export] public Label TooltipLabel { get; set; }
public override void _Ready()
{
TooltipLabel.Text = Text;
UiRoot.Visible = false;
Area.BodyEntered += OnBodyEntered;
Area.BodyExited += OnBodyExited;
}
private void OnBodyEntered(Node2D body)
{
ShowTooltip();
}
private void OnBodyExited(Node2D body)
{
HideTooltip();
}
private void ShowTooltip()
{
UiRoot.Visible = true;
}
private void HideTooltip()
{
UiRoot.Visible = false;
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class TrailComponent : Line2D
{
[Export] public int MaxPoints { get; set; } = 100;
private readonly Queue<Vector2> _queue = new();
public override void _Process(double delta)
{
if (Owner is Node2D root) _queue.Enqueue(root.GlobalPosition);
if (_queue.Count > MaxPoints) _queue.Dequeue();
ClearPoints();
foreach (var point in _queue) AddPoint(point);
}
}

View File

@@ -0,0 +1,25 @@
using Godot;
using Mr.BrickAdventures.scripts.interfaces;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class UnlockOnRequirementComponent : Node
{
[Export] public RequirementComponent RequirementComponent { get; set; }
[Export] public Node UnlockTarget { get; set; }
public override void _Ready()
{
RequirementComponent.RequirementMet += OnRequirementMet;
}
private void OnRequirementMet(CollectableType requirementType)
{
if (requirementType != RequirementComponent.RequirementType) return;
if (UnlockTarget is IUnlockable unlockable)
{
unlockable.Unlock();
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Mr.BrickAdventures.scripts.interfaces;
public interface IUnlockable
{
void Unlock();
}