using System; using System.Collections.Generic; using System.Linq; using Cirno.Scripts.Actors; using Cirno.Scripts.Components; using Cirno.Scripts.Resources; using Cirno.Scripts.Utils; using Godot; using Godot.Collections; using Array = System.Array; namespace Cirno.Scripts.AttackPatterns; [GlobalClass] [Tool] public partial class SpiralPattern : AttackPattern { [Export] public BulletResource BulletResource { get; set; } [Export] public Vector2 EmitterOffset { get; set; } = Vector2.Zero; //Export] public PackedScene BulletScene; //[Export] private float _bulletLifeTime = 20f; // Switch to res //[Export] private bool _destroyOnCollision = false; // Switch to res //[Export] private float bulletSpeed = 5f; // Switch to res [Export] public int bulletCount = 16; [Export] public float rotationSpeed = 0f; [Export] public float _rotationOffset = 0f; [Export] public float duration = 5f; [Export] public float spread = 360f; [Export] public float burstInterval = 0.5f; [ExportCategory("Burst")] [Export] public int ShotsPerBurst { get; private set; } = 100; [Export] public float BurstRate { get; private set; } = 0f; //[Export] private BulletOwner owner = BulletOwner.Enemy; // Switch to res //[Export] private DamageType _damageType = DamageType.Neutral; // Switch to res //[Export] private float _bulletDamage = 1f; // Switch to res // [ExportGroup("Modifiers")] [Export] private BulletCreationModifier _modifier; // [ExportGroup("Modifiers")] [Export] private Array _timeModifiers; [ExportCategory("Other")] [Export] public bool _targetPlayer = false; [ExportCategory("Other")] [Export] public bool _predictPlayer = false; [ExportCategory("Overrides")] [Export] public bool OverrideOwner { get; private set; } = false; [Export] public BulletOwner Owner { get; private set; } = BulletOwner.None; [Export] public bool OverrideDamageType { get; private set; } = false; [Export] public DamageType DamageType { get; private set; } = DamageType.Neutral; [Export] public bool OverrideControllable { get; private set; } = false; [Export] public bool Controllable { get; private set; } = false; [ExportCategory("Extra Modifiers")] [Export] public bool OverrideCreationModifier { get; private set; } = false; [Export] public BulletCreationModifier Modifier; [Export] public Array TimeModifiers = []; protected virtual BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f) { var bullet = this.BulletResource.MakeBullet(position, count, spread, rotationOffset); if (OverrideOwner) { bullet.Owner = this.Owner; } if (OverrideDamageType) { bullet.DamageType = DamageType; } if (OverrideControllable) { bullet.Controllabe = Controllable; } if (OverrideCreationModifier) { bullet.Modifier = this.Modifier; } if (TimeModifiers.Count != 0) { bullet.TimeModifiers.AddRange(TimeModifiers); } //var bl = BulletResource.MakeBullet(position, bulletCount, angleOffset); //bl.Direction = direction; return bullet; // return new BulletInfo() // { // Position = position, // Direction = direction, // LifeTime = _bulletLifeTime, // DestroyOnCollision = _destroyOnCollision, // Speed = bulletSpeed, // Owner = owner, // DamageType = _damageType, // Damage = _bulletDamage, // BulletCount = bulletCount, // Spread = spread, // BulletScene = BulletScene, // RotationOffset = angleOffset, // Modifier = _modifier, // TimeModifiers = _timeModifiers.ToList()// _timeModifiers.Select(x => x.MakeClone()).ToList() // // TimeModifiers = _timeModifiers?.Where(mod => mod != null).ToList() ?? new List() // }; } public override IPatternMachine MakeMachine(Node parent) { return new SpiralPatternMachine(this, parent); } public class SpiralPatternMachine(SpiralPattern pattern, Node parent) : IPatternMachine { public Node Parent => parent; public IScriptHost ScriptHost { get; private set; } private double timer; private double burstTimer; //private double _burstRateTimer; private BulletSpawner spawner; private ShootStatus _state = ShootStatus.Idle; private int _burstBullets; public void Start() { ScriptHost = Parent as IScriptHost; timer = 0; _burstBullets = pattern.ShotsPerBurst; burstTimer = pattern.burstInterval; // start immediately //_burstRateTimer = pattern.burstInterval; spawner = parent.GetNode("BulletSpawner"); _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) { _state = ShootStatus.WaitingReload; } else { _state = ShootStatus.WaitingBurst; } } public void UpdatePattern(double delta) { switch (_state) { case ShootStatus.Idle: case ShootStatus.Done: return; case ShootStatus.Shooting: ShootingUpdate(delta); break; case ShootStatus.WaitingBurst: WaitingBurstUpdate(delta); break; case ShootStatus.WaitingReload: WaitingReloadUpdate(delta); break; } if (timer >= pattern.duration) { _state = ShootStatus.Done; } // timer += delta; // burstTimer += delta; // //_burstRateTimer += delta; // // if (timer > pattern.duration) // { // return; // } // // if (burstTimer < pattern.burstInterval) return; // // Shoot(); // // // // burstTimer = 0; } private void Shoot() { float angleOffset = pattern._rotationOffset + (float)(pattern.rotationSpeed * timer); Vector2 direction = pattern.BulletResource.Direction; if (pattern._targetPlayer && GameManager.Instance.PlayerPosition.HasValue) { if (pattern._predictPlayer && GameManager.Instance.PlayerVelocity.HasValue) { var predictedDirection = MathFunctions.PredictInterceptPosition(ScriptHost.ParentObject.GlobalPosition, GameManager.Instance.PlayerPosition.Value, GameManager.Instance.PlayerVelocity.Value, pattern.BulletResource.BulletSpeed); if (predictedDirection.HasValue) { direction = (predictedDirection.Value - ScriptHost.ParentObject.GlobalPosition).Normalized(); } } else { direction = (GameManager.Instance.PlayerPosition.Value - ScriptHost.ParentObject.GlobalPosition).Normalized(); } } var bullet = pattern.MakeBullet(ScriptHost.ParentObject.GlobalPosition + pattern.EmitterOffset, pattern.bulletCount, pattern.spread, angleOffset); bullet.Direction = direction; //spawner.SpawnBullet(MakeBullet(Boss.GlobalPosition, direction, angleOffset)); spawner.SpawnBullet(bullet); } public bool IsComplete() { //return timer >= pattern.duration; return _state is ShootStatus.Done; } private enum ShootStatus { Idle, Shooting, WaitingBurst, WaitingReload, Done } } }