using Godot; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Cirno.Scripts; using Cirno.Scripts.Components; using Cirno.Scripts.Resources; public partial class Bullet : Area2D { [Export] public float Speed = 1900f; public BulletOwner BulletOwner => _bulletInfo?.Owner ?? BulletOwner.None; public float Damage => _bulletInfo?.Damage ?? 1; public DamageType DamageType => _bulletInfo?.DamageType ?? DamageType.Neutral; protected Vector2 _direction = Vector2.Right; private double _elapsedTime = 0f; private BulletInfo _bulletInfo; public BulletInfo BulletInfo => _bulletInfo; private List _modifiers = new(); private GameManager _gameManager; public void Initialize(BulletInfo bulletInfo, GameManager gameManager) { _bulletInfo = bulletInfo; _gameManager = gameManager; // Need to clone them here // _modifiers = _bulletInfo.TimeModifiers.Select(x => x.MakeClone()).ToList(); // var clonedModifiers = _bulletInfo.TimeModifiers.Select(x => x.MakeClone()); // _modifiers = clonedModifiers.ToList(); // Ugly hack to make instances unique _modifiers = _bulletInfo.TimeModifiers.Select(x => x.Wrap()).ToList(); } private void ApplyTimeModifiers(double delta) { foreach (var modifier in _modifiers) { if (_elapsedTime >= modifier.TimeModifier.TimeInSeconds) { if (!modifier.Applied) { modifier.Applied = true; modifier.TimeModifier.Start(this); } modifier.TimeModifier.Update(this, delta); // switch (modifier.ModifierType) // { // case TimeModifierType.SpeedChange: // //_bulletInfo.Speed += modifier.Value; // Speed = modifier.Value; // break; // case TimeModifierType.RotationChange: // RotateBullet(modifier.Value); // //Rotation += Mathf.DegToRad(modifier.Value); // break; // case TimeModifierType.FacePlayer: // FacePlayer(); // break; // } // if (!modifier.Continuous) // { // modifier.Applied = true; // } } } } public virtual void RotateBullet(float degrees) { float radians = Mathf.DegToRad(degrees); _direction = _direction.Rotated(radians).Normalized(); // Rotate direction SetRotation(Rotation + radians); //Rotation = radians; } public virtual void RotateSpriteDegrees(float degrees) { SetRotationDegrees(RotationDegrees + degrees); } public virtual void RotateSprite(float radians) { SetRotation(Rotation + radians); } public void FacePlayer() { if (_gameManager.Player != null) { _direction = (_gameManager.Player.GlobalPosition - this.GlobalPosition).Normalized(); RotateBullet(0); // quick hack to rotate lasers //LookAt(player.GlobalPosition); } } //private void OnBodyEntered(Node body) //{ // When a body is entered, invoke the event and pass the collided body // BulletHit?.Invoke(body); // Then remove the bullet // QueueFree(); //} public void SetDirection(Vector2 direction) { var normalized = direction.Normalized(); _direction = normalized; SetRotation(Mathf.Atan2(normalized.Y, normalized.X) + Mathf.Pi / 2); //Debug.WriteLine($"Bullet Shot at direction {direction.X} {direction.Y}"); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { _elapsedTime += delta; } public override void _PhysicsProcess(double delta) { if (_bulletInfo != null) { ApplyTimeModifiers(delta); } this.Position += ((float)(Speed * delta) * _direction); } private void _on_visible_on_screen_notifier_2d_screen_exited() { //Debug.WriteLine("Destroy bullet out of screen"); Destroy(); } private void _on_body_entered(Node2D body) { if (body.IsInGroup("Solid")) { //Debug.WriteLine("Collision"); Destroy(); } //// Do not Collide with body for purpose of destroying bullets // else if (body.IsInGroup("Destroyable")) // { // Debug.WriteLine("Collision with destroyable object body"); // QueueFree(); // } } private void _on_area_entered(Area2D area) { if (area.IsInGroup("Solid")) { Destroy(); return; } if (area.IsInGroup("Destroyable") && area is IDestructible destructible && CanHit(BulletOwner, destructible.BulletGroup)) { // hit destructible.Hit(Damage, DamageType); Destroy(); } } public bool CanHit(BulletOwner bulletOwner, BulletOwner targetGroup) { // If either is None, it always hits if (bulletOwner == BulletOwner.None || targetGroup == BulletOwner.None) { return true; } // Otherwise, it hits only if they are different groups return bulletOwner != targetGroup; } public void Destroy() { if (_bulletInfo?.DestructionParticlesScene != null) { var particle = this.CreateSibling(_bulletInfo.DestructionParticlesScene); particle.Init(); } QueueFree(); } } public enum BulletOwner { None, Player, Enemy } public enum DamageType { Neutral, Ballistic, Fire, Ice, Explosive, Acid }