using Godot; using testing.interfaces; public partial class Bullet : Area2D { [Export] public float Speed { get; set; } = 600.0f; [Export] public float Damage { get; set; } = 10.0f; public bool PierceEntity { get; set; } = false; public bool PierceTerrain { get; set; } = false; public bool ricochet { get; set; } = true; public int BounceLimit { get; set; } = 5; private int bounces; public Vector2 Direction { get; set; } = Vector2.Zero; public override void _Ready() { bounces = 0; if (Direction != Vector2.Zero) { Direction = Direction.Normalized(); Rotation = Direction.Angle(); } } public override void _PhysicsProcess(double delta) { if (Direction == Vector2.Zero) return; float remainingDistance = Speed * (float)delta; int maxSteps = 4; while (remainingDistance > 0.001f && maxSteps > 0) { maxSteps--; Vector2 motion = Direction * remainingDistance; // Cast a ray along the motion path plus a small buffer offset (5 units) Vector2 rayEnd = GlobalPosition + motion + Direction * 5.0f; var spaceState = GetWorld2D().DirectSpaceState; var query = PhysicsRayQueryParameters2D.Create(GlobalPosition, rayEnd, CollisionMask); query.Exclude = new Godot.Collections.Array { GetRid() }; query.HitFromInside = true; var result = spaceState.IntersectRay(query); if (result.Count == 0) { GlobalPosition += motion; break; } Vector2 hitPos = result["position"].AsVector2(); Vector2 hitNormal = result["normal"].AsVector2(); GodotObject colliderObj = result["collider"].AsGodotObject(); Node2D collider = colliderObj as Node2D; float distToHit = GlobalPosition.DistanceTo(hitPos); float distMoved = Mathf.Min(remainingDistance, distToHit); remainingDistance -= distMoved; if (collider is IDamageable target) { target.TakeDamage(Damage); if (!PierceEntity) { QueueFree(); return; } GlobalPosition = hitPos + Direction * 0.1f; } else { if (ricochet && bounces < BounceLimit) { if (hitNormal == Vector2.Zero) { Direction = -Direction; } else { Direction = Direction.Bounce(hitNormal).Normalized(); } Rotation = Direction.Angle(); bounces++; // Offset position slightly outward along hitNormal to avoid sticking inside geometry GlobalPosition = hitPos + (hitNormal != Vector2.Zero ? hitNormal : Direction) * 2.0f; } else if (!PierceTerrain) { QueueFree(); return; } else { GlobalPosition = hitPos + Direction * 0.1f; } } } } }