diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/Enemy.cs | 158 |
1 files changed, 151 insertions, 7 deletions
diff --git a/scripts/Enemy.cs b/scripts/Enemy.cs index cdbcd38..d2f6477 100644 --- a/scripts/Enemy.cs +++ b/scripts/Enemy.cs @@ -5,27 +5,171 @@ public partial class Enemy : CharacterBody2D, IDamageable { [Export] public float MoveSpeed { get; set; } = 150.0f; [Export] public float MaxHealth { get; set; } = 30.0f; + [Export] public float Damage { get; set; } = 50.0f; + [Export] public float AttackRange = 40.0f; // Distance to trigger swing + [Export] public float AttackCooldown = 1.2f; + [Export] public float SteeringAcceleration { get; set; } = 12.0f; // Higher = tighter turns, lower = smoother turns + [Export] public float SwingAngle = 150.0f; // The parent node holding the weapon - private Node2D _player; + private NavigationAgent2D _nav; + + [Export] public Node2D WeaponPivot; // The parent node holding the weapon + [Export] public Area2D WeaponHitbox; + private bool _isAttacking = false; + private bool _canAttack = true; + private Node2D _player; + private Vector2 _lastPlayerPos; + public override void _Ready() { - // Find the player automatically using the group we just set up - _player = GetTree().GetFirstNodeInGroup("player") as Node2D; + _nav = GetNode<NavigationAgent2D>("NavigationAgent2D"); + _player = GetTree().GetFirstNodeInGroup("player") as CharacterBody2D; + _lastPlayerPos = _player.GlobalPosition; + Callable.From(ActorSetup).CallDeferred(); + + _nav.AvoidanceEnabled = true; + + // Connect the VelocityComputed signal to receive safe avoidance vectors + _nav.VelocityComputed += OnVelocityComputed; + + if (WeaponHitbox != null) + { + // Keep hitbox turned off when not swinging + WeaponHitbox.Monitoring = false; + WeaponHitbox.BodyEntered += OnWeaponHitboxBodyEntered; + } + + } + + private async void ActorSetup() + { + await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame); + SetMovementTarget(_player.Position); + } + + public void SetMovementTarget(Vector2 movementTarget) + { + _nav.TargetPosition = movementTarget; } public override void _PhysicsProcess(double delta) { - if (_player == null) return; + if (!GodotObject.IsInstanceValid(_player) || _isAttacking) return; // Calculate direction vector pointing from the enemy to the player - Vector2 direction = GlobalPosition.DirectionTo(_player.GlobalPosition); + // if (GlobalPosition.DistanceSquaredTo(_lastPlayerPos) > 256.0f) + // { + // _lastPlayerPos = _player.GlobalPosition; + // _nav.TargetPosition = _lastPlayerPos; + // } + _nav.TargetPosition = _player.GlobalPosition; + float distanceToPlayer = GlobalPosition.DistanceTo(_player.GlobalPosition); + if (distanceToPlayer <= AttackRange) + { + if (_canAttack) + { + PerformSwingAttack(); + return; + } + } - // Move towards the player - Velocity = direction * MoveSpeed; + + if (_nav.IsNavigationFinished()) + { + _nav.Velocity = Vector2.Zero; + Velocity = Velocity.MoveToward(Vector2.Zero, (float)delta * MoveSpeed * SteeringAcceleration); + MoveAndSlide(); + return; + } + + Vector2 nextPathPosition = _nav.GetNextPathPosition(); + Vector2 desiredDirection = GlobalPosition.DirectionTo(nextPathPosition); + Vector2 targetVelocity = desiredDirection * MoveSpeed; + + WeaponPivot.Rotation = desiredDirection.Angle(); + + // 5. Pass target velocity to the NavigationAgent for processing + if (_nav.AvoidanceEnabled) + { + // Triggers OnVelocityComputed signal on the next frame calculation + _nav.Velocity = targetVelocity; + } + else + { + // Fallback smooth steering without avoidance + ApplySmoothSteering(targetVelocity, (float)delta); + } + } + private async void PerformSwingAttack(){ + _isAttacking = true; + _canAttack = false; + Velocity = Vector2.Zero; // Stop moving while swinging + + // 1. Calculate the angle pointing toward the player + float aimAngle = (_player.GlobalPosition - GlobalPosition).Angle(); + + // Define swing arc (-60 degrees back to +60 degrees forward) + float startAngle = aimAngle - Mathf.DegToRad(SwingAngle/2); + float endAngle = aimAngle + Mathf.DegToRad(SwingAngle/2); + + // 2. Set starting position for the swing + + + Tween prepTween = CreateTween(); + // 3. Create a Tween to animate the rotation + prepTween.TweenProperty(WeaponPivot, "rotation", startAngle, 0.25f) + .SetTrans(Tween.TransitionType.Quad) + .SetEase(Tween.EaseType.Out); + await ToSignal(prepTween, Tween.SignalName.Finished); + WeaponPivot.Rotation = startAngle; + WeaponHitbox.Monitoring = true; // Turn on collision check + + Tween swingTween = CreateTween(); + + // Rotate from startAngle to endAngle over 0.25 seconds with a smooth curve + swingTween.TweenProperty(WeaponPivot, "rotation", endAngle, 0.25f) + .SetTrans(Tween.TransitionType.Quad) + .SetEase(Tween.EaseType.Out); + + // Wait for the swing movement to complete + await ToSignal(swingTween, Tween.SignalName.Finished); + + // 4. Disable hitbox as soon as swing finishes + WeaponHitbox.Monitoring = false; + + // 5. Retract weapon back to neutral position smoothly + Tween resetTween = CreateTween(); + resetTween.TweenProperty(WeaponPivot, "rotation", aimAngle, 0.15f); + await ToSignal(resetTween, Tween.SignalName.Finished); + + _isAttacking = false; + + // Cooldown timer before the enemy can swing again + await ToSignal(GetTree().CreateTimer(AttackCooldown), SceneTreeTimer.SignalName.Timeout); + _canAttack = true; + } + + private void OnWeaponHitboxBodyEntered(Node2D body) + { + // Deal damage when the weapon hits a damageable target + if (body is IDamageable target) + { + target.TakeDamage(Damage); + } + } + private void OnVelocityComputed(Vector2 safeVelocity) + { + Velocity = safeVelocity; + MoveAndSlide(); + } + private void ApplySmoothSteering(Vector2 targetVelocity, float delta) + { + Velocity = Velocity.Lerp(targetVelocity, delta * SteeringAcceleration); MoveAndSlide(); } + public void TakeDamage(float amount) { MaxHealth -= amount; |
