Add new components: ExplosiveComponent, FadeAwayComponent, FireEffectComponent, FlipComponent, GravityMotionComponent, LaunchComponent, and update PlatformMovement with LastDirection property

This commit is contained in:
2025-08-10 17:53:06 +02:00
parent ac477115c5
commit 54ffa8a42c
7 changed files with 283 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class ExplosiveComponent : Node2D
{
[Export] public DamageComponent Damage { get; set; }
[Export] public Area2D Area { get; set; }
[Export] public Area2D ExplodeArea { get; set; }
[Export] public PackedScene ExplosionEffect { get; set; }
[Export] public float TimeToExplode { get; set; } = 9f;
[Signal] public delegate void OnExplosionEventHandler(Node2D body);
private Timer _timer;
public override void _Ready()
{
if (Damage != null)
{
GD.PushError("ExplosiveComponent: DamageComponent is not set.");
return;
}
if (ExplodeArea != null)
{
GD.PushError("ExplosiveComponent: ExplodeArea is not set.");
return;
}
Area.BodyEntered += OnAreaBodyEntered;
Area.AreaEntered += OnAreaAreaEntered;
}
private void OnAreaAreaEntered(Area2D area)
{
Explode();
}
private void OnAreaBodyEntered(Node2D body)
{
Explode();
}
private void PrepareTimer()
{
_timer = new Timer();
_timer.SetWaitTime(TimeToExplode);
_timer.OneShot = true;
_timer.Autostart = true;
_timer.Timeout += Explode;
AddChild(_timer);
}
private void Explode()
{
_timer.Stop();
if (ExplosionEffect != null)
{
var explosionInstance = ExplosionEffect.Instantiate<GpuParticles2D>();
if (Owner is Node2D root) explosionInstance.SetGlobalPosition(root.GlobalPosition);
GetTree().CurrentScene.AddChild(explosionInstance);
explosionInstance.SetEmitting(true);
}
var bodies = ExplodeArea.GetOverlappingBodies();
foreach (var body in bodies)
{
var health = body.GetNodeOrNull<HealthComponent>("HealthComponent");
if (Damage != null && health != null)
{
Damage.DealDamage(health);
}
EmitSignalOnExplosion(body);
}
Owner.QueueFree();
}
}

View File

@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class FadeAwayComponent : Node
{
[Export] public Sprite2D Sprite { get; set; }
[Export] public float FadeDuration { get; set; } = 1f;
[Export] public float Speed { get; set; } = 10f;
[Export] public Vector2 Direction { get; set; } = Vector2.Up;
[Export] public Area2D Area { get; set; }
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
_ = FadeAway();
}
private async Task FadeAway()
{
var tween = CreateTween().SetParallel(true);
tween.TweenProperty(Sprite, "modulate:a", 0f, FadeDuration);
tween.TweenProperty(Sprite, "position", Sprite.Position + (Direction * Speed), FadeDuration);
await ToSignal(tween, Tween.SignalName.Finished);
Owner.QueueFree();
}
}

View File

@@ -0,0 +1,71 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class FireEffectComponent : Node
{
[Export] public HealthComponent Health { get; set; }
[Export] public StatusEffectComponent StatusEffectComponent { get; set; }
[Export] public GpuParticles2D FireFX { get; set; }
private StatusEffectDataResource _data = null;
private bool _shouldDealDamage = false;
private double _timeElapsed = 0f;
public override void _Ready()
{
if (Health == null)
{
Health = GetNode<HealthComponent>("HealthComponent");
}
if (StatusEffectComponent == null)
{
StatusEffectComponent = GetNode<StatusEffectComponent>("StatusEffectComponent");
}
if (Health == null)
{
GD.PushError("FireEffectComponent: HealthComponent is not set.");
return;
}
if (StatusEffectComponent == null)
{
GD.PushError("FireEffectComponent: StatusEffectComponent is not set.");
return;
}
StatusEffectComponent.EffectApplied += OnEffectApplied;
StatusEffectComponent.EffectRemoved += OnEffectRemoved;
}
public override void _Process(double delta)
{
if (!_shouldDealDamage || _data == null || Health == null) return;
_timeElapsed += delta;
if (_timeElapsed >= 1f)
{
Health.DecreaseHealth(_data.DamagePerSecond);
_timeElapsed = 0f;
}
}
private void OnEffectApplied(StatusEffect statusEffect)
{
if (statusEffect.EffectData.Type != StatusEffectType.Fire) return;
_data = statusEffect.EffectData;
_shouldDealDamage = true;
FireFX?.SetEmitting(true);
}
private void OnEffectRemoved(StatusEffectType type)
{
if (type != StatusEffectType.Fire) return;
_shouldDealDamage = false;
_data = null;
FireFX?.SetEmitting(false);
}
}

View File

@@ -0,0 +1,36 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class FlipComponent : Node2D
{
[Export] public Sprite2D LeftEye { get; set; }
[Export] public Sprite2D RightEye { get; set; }
[Export] public PlatformMovement PlatformMovement { get; set; }
public override void _Process(double delta)
{
if (PlatformMovement == null) return;
var velocity = PlatformMovement.LastDirection;
switch (velocity.X)
{
case < 0f:
LeftEye.Frame = 1;
RightEye.Frame = 1;
LeftEye.FlipH = true;
RightEye.FlipH = true;
break;
case > 0f:
LeftEye.Frame = 1;
RightEye.Frame = 1;
LeftEye.FlipH = false;
RightEye.FlipH = false;
break;
default:
LeftEye.Frame = 0;
RightEye.Frame = 0;
break;
}
}
}

View File

@@ -0,0 +1,37 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class GravityMotionComponent : Node2D
{
[Export] public CharacterBody2D Body { get; set; }
[Export] public LaunchComponent LaunchComponent { get; set; }
[Export] public Vector2 Gravity { get; set; } = new Vector2(0, 1000f);
[Export] public Vector2 TargetDirection { get; set; } = Vector2.Up;
private Vector2 _velocity = Vector2.Zero;
public override void _Ready()
{
if (LaunchComponent == null) return;
var direction = LaunchComponent.InitialDirection.X > 0f ? TargetDirection : new Vector2(-TargetDirection.X, TargetDirection.Y);
direction = direction.Normalized();
_velocity = direction * LaunchComponent.Speed;
}
public override void _PhysicsProcess(double delta)
{
if (Body == null) return;
_velocity += Gravity * (float)delta;
Body.Velocity = _velocity;
Body.MoveAndSlide();
if (_velocity.LengthSquared() > 0.01f)
{
Body.Rotation = _velocity.Angle();
}
}
}

View File

@@ -0,0 +1,24 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class LaunchComponent : Node2D
{
[Export] public Vector2 InitialDirection { get; set; } = Vector2.Right;
[Export] public float Speed { get; set; } = 16f;
[Export] public Vector2 SpawnPosition { get; set; } = Vector2.Zero;
[Export] public float SpawnRotation { get; set; } = 0f;
public override void _Ready()
{
if (Owner is not Node2D root) return;
root.GlobalPosition = SpawnPosition;
root.GlobalRotation = SpawnRotation;
}
public Vector2 GetInitialVelocity()
{
return InitialDirection.Normalized() * Speed;
}
}

View File

@@ -39,6 +39,8 @@ public partial class PlatformMovement : Node2D, IMovement
private float _jumpGravity;
private float _fallGravity;
public Vector2 LastDirection => _lastDirection;
public override void _Ready()
{
base._Ready();