61 lines
2.2 KiB
GDScript
61 lines
2.2 KiB
GDScript
class_name BotSteering
|
|
extends RefCounted
|
|
|
|
var last_position := Vector3.INF
|
|
var stalled := 0.0
|
|
var repaths := 0
|
|
var life := -1
|
|
|
|
func needs_repath(p: Fighter, delta: float) -> bool:
|
|
if life != p.life:
|
|
life = p.life
|
|
last_position = p.global_position
|
|
stalled = 0
|
|
repaths = 0
|
|
if p.bot_path.is_empty():
|
|
stalled = 0
|
|
return false
|
|
if p.global_position.distance_to(last_position) > 0.3:
|
|
last_position = p.global_position
|
|
stalled = 0
|
|
repaths = 0
|
|
else:
|
|
stalled += delta
|
|
if stalled < 1.25: return false
|
|
stalled = 0
|
|
repaths += 1
|
|
return true
|
|
|
|
static func reached(position: Vector3, waypoint: Vector3) -> bool:
|
|
# Navigation surfaces sit one voxel above the physical floor. Compare height
|
|
# separately so stair landings do not leave an unreachable waypoint forever.
|
|
return Vector2(position.x - waypoint.x, position.z - waypoint.z).length() < 0.5 and absf(position.y - waypoint.y) < 0.65
|
|
|
|
static func clear_direction(p: Fighter, direction: Vector3) -> bool:
|
|
var pose := p.global_transform
|
|
pose.origin.y += 0.26
|
|
return not p.test_move(pose, direction * 0.35)
|
|
|
|
static func avoid(p: Fighter, desired: Vector3, roster: Array) -> Vector3:
|
|
var direction := Vector3(desired.x, 0, desired.z).normalized()
|
|
var side := Vector3(-direction.z, 0, direction.x)
|
|
var separation := Vector3.ZERO
|
|
for other: Fighter in roster:
|
|
if other == p or other.hp <= 0 or absf(other.position.y - p.position.y) > 1.2: continue
|
|
var offset := p.position - other.position
|
|
offset.y = 0
|
|
var distance := offset.length()
|
|
if distance > 1.8: continue
|
|
var away := offset.normalized() if distance > 0.01 else side * (1 if p.peer_id > other.peer_id else -1)
|
|
separation += away * maxf(0, 1.3 - distance)
|
|
# Both approaching actors keep right; unlike random strafe they do not
|
|
# mirror one another into the same gap each frame.
|
|
if direction.dot(-away) > 0.2: separation += side * (1.8 - distance)
|
|
var candidate := (direction + separation * 1.4).normalized()
|
|
if clear_direction(p, candidate): return candidate
|
|
if clear_direction(p, direction): return direction
|
|
for angle in [0.7, -0.7, 1.3, -1.3]:
|
|
var alternative := direction.rotated(Vector3.UP, angle)
|
|
if clear_direction(p, alternative): return alternative * 0.65
|
|
return Vector3.ZERO
|