Add classic HUD, auto reload, furniture pathfinding and death flatline
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
extends Node
|
||||
## Shared planar A* map for static office collisions; no runtime navmesh bake.
|
||||
const CELL := 0.5
|
||||
const CLEARANCE := 0.60 # capsule radius .35 + conservative cell/edge margin
|
||||
var grid := AStarGrid2D.new()
|
||||
var obstacles: Array[Rect2] = []
|
||||
|
||||
func _ready() -> void:
|
||||
name = "RoomNavigation"
|
||||
grid.region = Rect2i(-17, -17, 35, 35)
|
||||
grid.cell_size = Vector2(CELL, CELL)
|
||||
grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES
|
||||
grid.default_compute_heuristic = AStarGrid2D.HEURISTIC_OCTILE
|
||||
grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE
|
||||
grid.update() # Must precede solid flags; update clears them.
|
||||
for prop in get_tree().get_nodes_in_group("office_props"):
|
||||
for collider in prop.get_children():
|
||||
if not collider is CollisionShape3D or not collider.shape is BoxShape3D:
|
||||
continue
|
||||
var half: Vector3 = collider.shape.size * 0.5
|
||||
var minimum := Vector2(INF, INF)
|
||||
var maximum := Vector2(-INF, -INF)
|
||||
for x in [-half.x, half.x]:
|
||||
for z in [-half.z, half.z]:
|
||||
var p: Vector3 = collider.global_transform * Vector3(x, 0, z)
|
||||
minimum = minimum.min(Vector2(p.x, p.z))
|
||||
maximum = maximum.max(Vector2(p.x, p.z))
|
||||
obstacles.append(Rect2(minimum, maximum - minimum).grow(CLEARANCE))
|
||||
for x in range(-17, 18):
|
||||
for y in range(-17, 18):
|
||||
var id := Vector2i(x, y)
|
||||
var p := Vector2(id) * CELL
|
||||
for rect in obstacles:
|
||||
if rect.has_point(p):
|
||||
grid.set_point_solid(id)
|
||||
break
|
||||
|
||||
func _free_id(point: Vector3) -> Vector2i:
|
||||
var id := Vector2i(roundi(point.x / CELL), roundi(point.z / CELL))
|
||||
id.x = clampi(id.x, -17, 17)
|
||||
id.y = clampi(id.y, -17, 17)
|
||||
if not grid.is_point_solid(id):
|
||||
return id
|
||||
var best := id
|
||||
var distance := INF
|
||||
for x in range(-17, 18):
|
||||
for y in range(-17, 18):
|
||||
var candidate := Vector2i(x, y)
|
||||
if not grid.is_point_solid(candidate):
|
||||
var d := Vector2(candidate).distance_squared_to(Vector2(point.x, point.z) / CELL)
|
||||
if d < distance:
|
||||
best = candidate
|
||||
distance = d
|
||||
return best
|
||||
|
||||
func route(from: Vector3, to: Vector3) -> PackedVector3Array:
|
||||
var result := PackedVector3Array()
|
||||
for p in grid.get_point_path(_free_id(from), _free_id(to)):
|
||||
result.append(Vector3(p.x, 0, p.y))
|
||||
return result
|
||||
|
||||
func direct_clear(from: Vector3, to: Vector3) -> bool:
|
||||
var a := Vector2(from.x, from.z)
|
||||
var b := Vector2(to.x, to.z)
|
||||
var count := maxi(1, ceili(a.distance_to(b) / 0.15))
|
||||
for i in range(count + 1):
|
||||
var p := a.lerp(b, float(i) / count)
|
||||
if absf(p.x) > 8.6 or absf(p.y) > 8.6:
|
||||
return false
|
||||
for obstacle in obstacles:
|
||||
if obstacle.has_point(p):
|
||||
return false
|
||||
return true
|
||||
Reference in New Issue
Block a user