mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 10:45:33 +00:00
222 lines
No EOL
7.1 KiB
C#
222 lines
No EOL
7.1 KiB
C#
using Cirno.Scripts.Actors;
|
|
using Cirno.Scripts.Components;
|
|
using Cirno.Scripts.Enums;
|
|
using Cirno.Scripts.Utils;
|
|
using Godot;
|
|
|
|
namespace Cirno.Scripts.AttackPatterns;
|
|
|
|
[GlobalClass]
|
|
[Tool]
|
|
public partial class LaserPattern : ShootingPattern3D
|
|
{
|
|
[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
|
|
[ExportGroup("Laser")][Export] public Color LethalColor { get; set; } = Colors.Red; // Thicker beam
|
|
|
|
protected override BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
|
|
{
|
|
var bf = base.MakeBullet(position, count, spread, rotationOffset);
|
|
|
|
bf.IsLaser = true;
|
|
bf.PreFireTime = PreFireTime;
|
|
bf.LethalTime = LethalTime;
|
|
bf.PreFireColor = PreFireColor;
|
|
bf.LethalColor = LethalColor;
|
|
bf.SpawnDelay = SpawnDelay;
|
|
|
|
return bf;
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
} |