155 lines
3.8 KiB
C#
155 lines
3.8 KiB
C#
using System;
|
|
using Godot;
|
|
using Movementtests.interfaces;
|
|
|
|
[GlobalClass]
|
|
public partial class Enemy : CharacterBody3D,
|
|
IDamageable,
|
|
IDamageDealer,
|
|
IHealthable,
|
|
IKillable,
|
|
IMoveable,
|
|
ISpawnable,
|
|
IKnockbackable
|
|
{
|
|
// Signals and events
|
|
public event Action<IDamageable, DamageRecord> DamageTaken;
|
|
public event Action<IHealthable, float> HealthChanged;
|
|
public event Action<IHealthable> HealthDepleted;
|
|
|
|
// Public export components
|
|
[Export]
|
|
public Node3D Target { get; set; }
|
|
|
|
[ExportGroup("Health")]
|
|
[Export]
|
|
public Node CHealth { get; set; }
|
|
[Export]
|
|
public RHealth RHealth { get; set; }
|
|
|
|
[Export]
|
|
public RDeathEffect[] DeathEffects { get; set; }
|
|
|
|
[ExportGroup("Damage")]
|
|
[Export]
|
|
public Node CDamage { get; set; }
|
|
[Export]
|
|
public RDamage RDamage { get; set; }
|
|
[Export]
|
|
public Node CKnockback { get; set; }
|
|
[Export]
|
|
public RKnockback RKnockback { get; set; }
|
|
|
|
[ExportGroup("Movement")]
|
|
[Export]
|
|
public Node CMovement { get; set; }
|
|
[Export]
|
|
public RMovement RMovement { get; set; }
|
|
|
|
// Public stuff
|
|
public float CurrentHealth { get; set; }
|
|
|
|
// Private stuff
|
|
private Area3D _damageBox;
|
|
|
|
public override void _Ready()
|
|
{
|
|
Initialize();
|
|
SetupSignals();
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
_damageBox = GetNode<Area3D>("DamageBox");
|
|
|
|
if (CMovement is IMoveable moveable && RMovement != null) moveable.RMovement = RMovement;
|
|
if (CHealth is IHealthable healthable && RHealth != null)
|
|
{
|
|
healthable.RHealth = RHealth;
|
|
healthable.CurrentHealth = RHealth.StartingHealth;
|
|
}
|
|
if (CKnockback is IKnockbackable knockbackable && RKnockback != null) knockbackable.RKnockback = RKnockback;
|
|
}
|
|
|
|
public void SetupSignals()
|
|
{
|
|
_damageBox.BodyEntered += OnDamageBoxTriggered;
|
|
if (CDamage is IDamageable damageable)
|
|
{
|
|
damageable.DamageTaken += ReduceHealth;
|
|
damageable.DamageTaken += RegisterKnockback;
|
|
}
|
|
if (CHealth is IHealthable healthable) healthable.HealthDepleted += Kill;
|
|
}
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
var targetPlanar = new Vector3(Target.GlobalPosition.X, GlobalPosition.Y, Target.GlobalPosition.Z);
|
|
LookAt(targetPlanar);
|
|
|
|
var inputs = new MovementInputs(
|
|
Velocity: Velocity,
|
|
TargetLocation: Target.GlobalPosition,
|
|
isOnFloor: IsOnFloor(),
|
|
gravity: GetGravity(),
|
|
delta: delta
|
|
);
|
|
Velocity = ComputeVelocity(inputs);
|
|
Velocity += ComputeKnockback();
|
|
MoveAndSlide();
|
|
}
|
|
|
|
public Vector3 ComputeVelocity(MovementInputs inputs)
|
|
{
|
|
if (CMovement is not IMoveable movement) return Vector3.Zero;
|
|
return movement!.ComputeVelocity(inputs);
|
|
}
|
|
|
|
public void OnDamageBoxTriggered(Node3D body)
|
|
{
|
|
if (body is not IDamageable damageable) return;
|
|
|
|
var damageRecord = new DamageRecord(this, RDamage);
|
|
damageable.TakeDamage(damageRecord);
|
|
}
|
|
|
|
public DamageRecord TakeDamage(DamageRecord damageRecord)
|
|
{
|
|
if (CDamage is not IDamageable damageable)
|
|
return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) };
|
|
|
|
var finalDamage = damageable.TakeDamage(damageRecord);
|
|
DamageTaken?.Invoke(this, finalDamage);
|
|
|
|
GD.Print($"Received damage: {finalDamage.Damage.DamageDealt}");
|
|
return finalDamage;
|
|
}
|
|
|
|
public void ReduceHealth(IDamageable source, DamageRecord damageRecord)
|
|
{
|
|
if (CHealth is not IHealthable healthable) return;
|
|
healthable.ReduceHealth(source, damageRecord);
|
|
}
|
|
|
|
public void Kill(IHealthable source)
|
|
{
|
|
foreach (var killable in DeathEffects.ToIKillables())
|
|
{
|
|
killable.Kill(source);
|
|
}
|
|
QueueFree();
|
|
}
|
|
|
|
public void RegisterKnockback(IDamageable source, DamageRecord damageRecord)
|
|
{
|
|
if (CKnockback is not IKnockbackable knockbackable) return;
|
|
knockbackable.RegisterKnockback(source, damageRecord);
|
|
}
|
|
|
|
public Vector3 ComputeKnockback()
|
|
{
|
|
if (CKnockback is not IKnockbackable knockbackable) return Vector3.Zero;
|
|
return knockbackable.ComputeKnockback();
|
|
}
|
|
}
|