Rotating turret NPC

This commit is contained in:
Marco 2025-03-03 15:01:12 +01:00
commit dc55fa97d3
14 changed files with 401 additions and 27 deletions

View file

@ -17,7 +17,7 @@ public partial class ActorFreeMovement : MovementHandler
set => _parent.MovementDirection = value;
}
[Export] public string StrafeAction { get; private set; } = "strafe";
[Export] public StringName StrafeAction { get; private set; } = "strafe";
public bool IsDestroyed => _parent.IsDestroyed;

View file

@ -12,9 +12,9 @@ public partial class EnemyPossessionMovement : ActorFreeMovement
[Export]
public AnimatedSprite2D PossessionSprite { get; private set; }
[Export] public string ControlEndAction { get; private set; } = "pause";
[Export] public StringName ControlEndAction { get; private set; } = "pause";
[Export] public string ShootAction { get; private set; } = "shoot";
[Export] public StringName ShootAction { get; private set; } = "shoot";
[Export] public DamageReceiverActorModule DamageReceiver { get; private set; }

View file

@ -0,0 +1,98 @@
using Godot;
namespace Cirno.Scripts.Components.Actors;
public partial class EnemyTurretRotationMovement : MovementHandler
{
public override Vector2 MovementDirection
{
get => _parent.MovementDirection;
set => _parent.MovementDirection = value;
}
public override Vector2 FacingDirection
{
get => _parent.FacingDirection;
set => _parent.FacingDirection = value;
}
[Export] public float PlayerDisengageRange = 500f;
[Export(PropertyHint.Layers2DPhysics)]
public uint CollisionMask { get; set; }
public bool IsDestroyed => _parent.IsDestroyed;
[Export] public Weapon EquippedWeapon;
[Export] private PlayerDetection _playerDetection;
private Vector2? _lastPlayerPosition = null;
private ActorAi _actorAi;
private bool IsPlayerInRange => _playerDetection is { IsPlayerInRange: true };
private bool IsPlayerInSight =>
_playerDetection is not null && _playerDetection.IsPlayerInSight(CollisionMask);
public override void Init(Actor parent)
{
base.Init(parent);
MovementDirection = Vector2.Zero;
FacingDirection = Vector2.Down;
_actorAi = parent.GetNode<ActorAi>("ActorAi");
}
public override void Update(double delta)
{
}
public override void PhysicsUpdate(double delta)
{
if (IsDestroyed) return;
if (_actorAi.Ai is not AiState.Enabled)
return;
switch (_actorAi.State)
{
case EnemyState.Idle:
if (_playerDetection != null && IsPlayerInSight)
{
_actorAi.State = EnemyState.Alert;
GD.Print("Switching to alert");
}
break;
case EnemyState.Alert:
// Update last known player position if it's in range
if (IsPlayerInRange)
{
_lastPlayerPosition = _playerDetection.CachedPlayer.GlobalPosition;
}
if (IsPlayerInSight)
{
Shoot();
}
break;
}
}
private void Shoot()
{
if (EquippedWeapon == null || !_lastPlayerPosition.HasValue) return;
var direction = (_lastPlayerPosition.Value - _parent.GlobalPosition).Normalized();
// Shoot at the player's last known position
EquippedWeapon.ShootDirection = direction;
FacingDirection = direction;
EquippedWeapon.Shoot();
}
}

View file

@ -0,0 +1 @@
uid://b0qcrs74bdqhf

View file

@ -0,0 +1,59 @@
using Godot;
using System;
namespace Cirno.Scripts.Components.Actors;
public partial class TurretAnimationModule : ActorModule
{
[Export] public AnimatedSprite2D TurretSprite;
[Export(PropertyHint.None, "suffix:°")] public float SweepAngle = 360f;
[Export(PropertyHint.None, "suffix:°")] public float RotationOffset = 0f;
[Export]
public bool InvertRotation = false;
private int _frameCount;
private float _anglePerFrame;
private Actor _actor;
public override void Init(Actor actor)
{
_actor = actor;
if (TurretSprite == null)
{
GD.PushError("TurretAnimationModule requires an AnimatedSprite2D reference.");
return;
}
_frameCount = TurretSprite.SpriteFrames.GetFrameCount(TurretSprite.Animation);
if (_frameCount == 0)
{
GD.PushError("TurretAnimatedSprite2D has no frames in the selected animation.");
return;
}
_anglePerFrame = SweepAngle / _frameCount;
}
public override void Update(double delta) { }
public override void PhysicsUpdate(double delta)
{
if (TurretSprite == null || _frameCount == 0) return;
Vector2 facingDirection = _actor.FacingDirection;
float angle = Mathf.RadToDeg(facingDirection.Angle()) + RotationOffset;
if (InvertRotation)
{
angle = -angle;
}
if (SweepAngle < 360f)
{
angle = Mathf.Clamp(angle, -SweepAngle / 2, SweepAngle / 2);
}
int frame = Mathf.Wrap((int)Mathf.Round(angle / _anglePerFrame), 0, _frameCount);
TurretSprite.Frame = frame;
}
}

View file

@ -0,0 +1 @@
uid://daoxbq4sxy0br