using System; using Godot; using Movementtests.interfaces; [GlobalClass] public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealthable, IKillable, IMoveable, ISpawnable { public event Action DamageTaken; public event Action HealthChanged; public event Action HealthDepleted; [Export] public Node3D Target { get; set; } [Export] public Node CHealth { get; set; } [Export] public RHealth RHealth { get; set; } public float CurrentHealth { get; set; } [Export] public Node CDamage { get; set; } [Export] public RDamage RDamage { get; set; } [Export] public Node CMovement { get; set; } [Export] public RMovement RMovement { get; set; } [Export] public RDeathEffect[] DeathEffects { get; set; } private Area3D _damageBox; public override void _Ready() { Initialize(); SetupSignals(); } public void Initialize() { _damageBox = GetNode("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; } } public void SetupSignals() { _damageBox.BodyEntered += OnDamageBoxTriggered; if (CDamage is IDamageable damageable) damageable.DamageTaken += ReduceHealth; 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); MoveAndSlide(); } public Vector3 ComputeVelocity(MovementInputs inputs) { if (CMovement is not IMoveable movement) return Vector3.Zero; return movement!.ComputeVelocity(inputs); } public void OnDamageBoxTriggered(Node3D body) { GD.Print("Take this!"); if (body is not IDamageable damageable) return; damageable.TakeDamage(RDamage); } public float TakeDamage(RDamage damage) { GD.Print("Ouch!"); if (CDamage is not IDamageable damageable) return 0; var finalDamage = damageable.TakeDamage(damage); DamageTaken?.Invoke(this, damage.DamageDealt); return finalDamage; } public void ReduceHealth(IDamageable source, float amount) { if (CHealth is not IHealthable healthable) return; healthable.ReduceHealth(source, amount); } public void Kill(IHealthable source) { foreach (var killable in DeathEffects.ToIKillables()) { killable.Kill(source); } QueueFree(); } }