blob: 9202e3a57625724c27a4f6a91eea8e75cd011bc7 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
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<Rid> { 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;
}
}
}
}
}
|