road-rage-tank/levels/enemy_spawner.gd

84 lines
2 KiB
GDScript

extends Node3D
var boxes: Array[CSGBox3D]
var points: Array[Vector3]
const MAX_CACHED_POINTS := 100
var doomed_point_idx := 0
@export var enemy_scenes: Array[EnemyEntry]
var weights: Array[float]
var total_cost := 0.
var money := 5.
var rng := RandomNumberGenerator.new()
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)
for entry in enemy_scenes:
total_cost += entry.cost
for entry in enemy_scenes:
weights.push_back(total_cost - entry.cost + 1.)
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 not Level.is_active(): return
money += 2.
if randf() > .1: return
if points.is_empty():
push_warning("tried to spawn enemy, but can't find a point!")
return
while money > 3.:
var entry: EnemyEntry = enemy_scenes.pick_random()
if entry.cost > money:
return
var scene := entry.scene
var pos: Vector3 = points.pick_random()
pos += Vector3.UP * 0.02
var car: Node3D = scene.instantiate()
car.position = pos
get_tree().current_scene.add_child(car)
money -= entry.cost