60 lines
1.5 KiB
GDScript
60 lines
1.5 KiB
GDScript
extends Node3D
|
|
|
|
var boxes: Array[CSGBox3D]
|
|
var points: Array[Vector3]
|
|
|
|
const MAX_CACHED_POINTS := 100
|
|
var doomed_point_idx := 0
|
|
|
|
func _get_point() -> Vector3:
|
|
assert(not boxes.is_empty())
|
|
|
|
var box: CSGBox3D = boxes.pick_random()
|
|
var x_stride := box.size.x / 2
|
|
var y_stride := box.size.y / 2
|
|
|
|
var ret := box.global_position
|
|
ret.x += randf_range(-x_stride, x_stride)
|
|
ret.y += randf_range(-y_stride, y_stride)
|
|
|
|
return ret
|
|
|
|
func _ready() -> void:
|
|
for child in get_children():
|
|
if child is CSGBox3D:
|
|
boxes.push_back(child)
|
|
|
|
func _process(_delta: float) -> void:
|
|
var v := _get_point()
|
|
v.y = 50.
|
|
|
|
var col_mask := 16 # floor collision layer
|
|
var params := PhysicsRayQueryParameters3D.create(
|
|
v, v + Vector3.DOWN * 100., 1 # detect any level geometry
|
|
)
|
|
var raycast := get_world_3d().direct_space_state.intersect_ray(params)
|
|
|
|
# if the raycast hit and the hit was the floor...
|
|
if not raycast.is_empty() and raycast.collider.collision_layer & col_mask:
|
|
var hitpos: Vector3 = raycast.position
|
|
|
|
if points.size() < MAX_CACHED_POINTS:
|
|
points.push_back(hitpos)
|
|
else:
|
|
points[doomed_point_idx] = hitpos
|
|
doomed_point_idx += 1
|
|
doomed_point_idx = doomed_point_idx % MAX_CACHED_POINTS
|
|
|
|
func _on_timer_timeout() -> void:
|
|
if points.is_empty():
|
|
push_warning("tried to spawn enemy, but can't find a point!")
|
|
return
|
|
|
|
var pos: Vector3 = points.pick_random()
|
|
pos += Vector3.UP * 0.02
|
|
var scene := preload("res://enemies/police_car/police_car.tscn")
|
|
var car: Node3D = scene.instantiate()
|
|
car.position = pos
|
|
get_tree().current_scene.add_child(car)
|
|
|
|
|