cirnogodot/Scripts/AttackPatterns/LaserPattern.cs

222 lines
7.1 KiB
C#
Raw Normal View History

2026-01-31 10:23:10 +01:00
using Cirno.Scripts.Actors;
using Cirno.Scripts.Components;
using Cirno.Scripts.Enums;
using Cirno.Scripts.Utils;
2025-02-14 10:59:32 +01:00
using Godot;
namespace Cirno.Scripts.AttackPatterns;
[GlobalClass]
2025-06-03 10:11:09 +02:00
[Tool]
2026-01-31 10:23:10 +01:00
public partial class LaserPattern : ShootingPattern3D
2025-02-14 10:59:32 +01:00
{
[ExportGroup("Laser")][Export] public float SpawnDelay { get; set; } = 0.3f; // Delay before beam appears
[ExportGroup("Laser")][Export] public float PreFireTime { get; set; } = 0.5f; // Time before laser becomes lethal
[ExportGroup("Laser")][Export] public float LethalTime { get; set; } = 1.5f; // Time laser remains lethal
[ExportGroup("Laser")][Export] public Color PreFireColor { get; set; } = new Color(1, 0, 0, 0.5f); // Thin red beam
2026-01-31 10:23:10 +01:00
[ExportGroup("Laser")][Export] public Color LethalColor { get; set; } = Colors.Red; // Thicker beam
2025-02-14 10:59:32 +01:00
2025-03-18 14:58:18 +01:00
protected override BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
2025-02-14 10:59:32 +01:00
{
2025-03-18 14:58:18 +01:00
var bf = base.MakeBullet(position, count, spread, rotationOffset);
2025-02-14 10:59:32 +01:00
bf.IsLaser = true;
bf.PreFireTime = PreFireTime;
bf.LethalTime = LethalTime;
bf.PreFireColor = PreFireColor;
bf.LethalColor = LethalColor;
bf.SpawnDelay = SpawnDelay;
return bf;
}
2026-01-31 10:23:10 +01:00
public override IPatternMachine MakeMachine(Node parent)
{
return new LaserPatternMachine(this, parent);
}
public class LaserPatternMachine(LaserPattern pattern, Node parent) : IPatternMachine
{
public Node Parent => parent;
public IScriptHost3D ScriptHost { get; private set; }
private double _timer;
private double _burstTimer;
private BulletSpawner3D _spawner;
private ShootStatus _state = ShootStatus.Idle;
private int _burstBullets;
private int _currentBurstOffset;
public void Start()
{
ScriptHost = Parent as IScriptHost3D;
_timer = 0;
_burstBullets = pattern.ShotsPerBurst;
_burstTimer = pattern.burstInterval;
_spawner = parent.GetNode<BulletSpawner3D>("BulletSpawner3D");
_state = pattern.Delay == 0 ? ShootStatus.Shooting : ShootStatus.Idle;
}
public void UpdatePattern(double delta)
{
switch (_state)
{
case ShootStatus.Idle:
IdleUpdate(delta);
break;
case ShootStatus.Done:
return;
case ShootStatus.Shooting:
ShootingUpdate(delta);
break;
case ShootStatus.WaitingBurst:
WaitingBurstUpdate(delta);
break;
case ShootStatus.WaitingReload:
WaitingReloadUpdate(delta);
break;
}
if (pattern.duration > -1 && _timer >= pattern.duration)
{
_state = ShootStatus.Done;
}
}
private void IdleUpdate(double delta)
{
_timer += delta;
if (_timer >= pattern.Delay)
{
_state = ShootStatus.Shooting;
}
}
private void WaitingBurstUpdate(double delta)
{
_timer += delta;
_burstTimer += delta;
if (_burstTimer >= pattern.burstInterval)
{
_state = ShootStatus.Shooting;
}
}
private void WaitingReloadUpdate(double delta)
{
_timer += delta;
_burstTimer += delta;
if (_burstTimer >= pattern.BurstRate)
{
_burstBullets = pattern.ShotsPerBurst;
_state = ShootStatus.Shooting;
}
}
private void ShootingUpdate(double delta)
{
_timer += delta;
_burstTimer = 0;
Shoot();
_burstBullets--;
if (_burstBullets <= 0)
{
if (pattern.LoopType == LoopType.PlayOnce)
{
_state = ShootStatus.Done;
return;
}
_state = ShootStatus.WaitingReload;
_currentBurstOffset++;
}
else
{
_state = ShootStatus.WaitingBurst;
}
}
private void Shoot()
{
if (pattern.BulletResource == null)
{
GD.PushError("LaserPattern: BulletResource is null! Cannot spawn laser.");
_state = ShootStatus.Done;
return;
}
float angleOffset = pattern._rotationOffset + (float)(pattern.rotationSpeed * _timer) +
(float)pattern.BurstRotationSpeed * _currentBurstOffset;
Vector2 direction = pattern.BulletResource.Direction;
// Rotate with parent rotation
if (pattern.UseParentRotationOffset)
{
direction = direction.Rotated(-_spawner.GlobalRotation.Y + Mathf.DegToRad(90));
}
// Handle player targeting for 3D
if (pattern._targetPlayer && GameController.Instance.PlayerPosition.HasValue)
{
if (pattern._predictPlayer && GameController.Instance.PlayerVelocity.HasValue)
{
var predictedDirection = MathFunctions.PredictInterceptPosition(
_spawner.GlobalPosition.ToVector2(),
GameController.Instance.PlayerPosition.Value.ToVector2(),
GameController.Instance.PlayerVelocity.Value.ToVector2(),
pattern.BulletResource.BulletSpeed);
if (predictedDirection.HasValue)
{
direction = (predictedDirection.Value - _spawner.GlobalPosition.ToVector2()).Normalized();
}
}
else
{
direction = (GameController.Instance.PlayerPosition.Value.ToVector2() -
_spawner.GlobalPosition.ToVector2()).Normalized();
}
}
var spawnPosition = _spawner.GlobalPosition + pattern.EmitterOffset;
// Create laser bullet with laser-specific properties
var bullet = pattern.MakeBullet(
spawnPosition.ToVector2(),
pattern.bulletCount,
pattern.spread,
angleOffset);
bullet.Direction = direction;
_spawner.SpawnBullet(bullet, spawnPosition);
}
public bool IsComplete()
{
if (!pattern.WaitForCompletion) return _state is ShootStatus.Done;
if (_state is not ShootStatus.Done) return false;
if (pattern.duration > -1)
{
return (_timer >= pattern.duration);
}
return true;
}
private enum ShootStatus
{
Idle,
Shooting,
WaitingBurst,
WaitingReload,
Done
}
}
2025-02-14 10:59:32 +01:00
}