188 lines
5.7 KiB
GDScript
188 lines
5.7 KiB
GDScript
extends Node3D
|
|
|
|
const BASE_DIMENSION: int = 2
|
|
|
|
@export var path_count: int = 5
|
|
var dimension: int = BASE_DIMENSION
|
|
|
|
@export var ped_spawn_rate: int = 20
|
|
# Spawn Rate = rate - floor(stage_count * stage_mod)
|
|
@export var ped_spawn_rate_stage_modifier: float = 0.5
|
|
@export var speed_boost_spawn_rate = 1
|
|
@export var bonus_time_spawn_rate = 1
|
|
|
|
var pedestrian_scn = preload("res://pedestrian.tscn")
|
|
var chunk_scn = preload("res://chunk.tscn")
|
|
var station_scn = preload("res://station.tscn")
|
|
var spawn_scn = preload("res://spawn.tscn")
|
|
var bonus_time_scn = preload("res://pickup/bonus_time.tscn")
|
|
var speed_boost_scn = preload("res://pickup/speed_boost.tscn")
|
|
|
|
func dir_to_vector(dir: int) -> Vector2i:
|
|
assert(dir >= 0 and dir <= 3)
|
|
var polarity = dir % 2 # 0: negative, 1: positive
|
|
var axis = floor(dir / 2) # 0: vertical, 1: horizontal
|
|
return Vector2i(
|
|
axis * (polarity * 2 - 1),
|
|
(1 - axis) * (polarity * 2 -1),
|
|
)
|
|
|
|
func vector_to_dir(vec: Vector2i) -> int:
|
|
assert(vec.length() == 1)
|
|
if vec == Vector2i(0, -1):
|
|
return 0
|
|
elif vec == Vector2i(0, 1):
|
|
return 1
|
|
elif vec == Vector2i(-1, 0):
|
|
return 2
|
|
elif vec == Vector2i(1, 0):
|
|
return 3
|
|
assert(false)
|
|
return -1
|
|
|
|
func _ready() -> void:
|
|
initialize_world()
|
|
|
|
func initialize_world() -> void:
|
|
dimension = BASE_DIMENSION + $Player.stage_counter
|
|
|
|
# ---- Decide position of Spawn & Station ----
|
|
var station = Vector2i(randi() % dimension, 0)
|
|
var spawn = Vector2i(dimension - station.x - 1, dimension - 1)
|
|
|
|
Global.station_coords = Vector3(
|
|
station.x * Global.chunk_size,
|
|
0,
|
|
station.y * Global.chunk_size,
|
|
)
|
|
|
|
$Player.position.x = spawn.x * Global.chunk_size
|
|
$Player.position.z = spawn.y * Global.chunk_size
|
|
|
|
# ---- Generate the paths ----
|
|
var paths: Array = []
|
|
for path_idx in range(path_count):
|
|
# Station's and the chunk below are always the same.
|
|
var path: Array[Vector2i] = [
|
|
station,
|
|
Vector2i(station.x, station.y + 1),
|
|
]
|
|
|
|
while true: # Decide each of the steps
|
|
var last_pos = path.slice(-1)[0]
|
|
var next_dir = dir_to_vector(randi() % 3 + 1) # cannot go up
|
|
var next_pos = (last_pos + next_dir).clampi(0,dimension-1)
|
|
|
|
if next_pos in path: # Invalid path, try again
|
|
continue
|
|
|
|
path.append(next_pos)
|
|
|
|
if next_pos == spawn or next_pos.y == dimension-1: # End of path
|
|
break
|
|
|
|
paths.append(path)
|
|
|
|
# ---- Instantiate chunks, station & spawn ----
|
|
var chunks: Array # Array[int(x)][int(y)][station_scn|chunk_scn|spawn_scn]
|
|
for x in range(dimension):
|
|
var row: Array
|
|
for y in range(dimension):
|
|
var new_chunk = null
|
|
var can_spawn_pickup = false
|
|
if Vector2i(x,y) == station:
|
|
new_chunk = station_scn.instantiate()
|
|
elif Vector2i(x,y) == spawn:
|
|
new_chunk = spawn_scn.instantiate()
|
|
can_spawn_pickup = true
|
|
else:
|
|
new_chunk = chunk_scn.instantiate()
|
|
can_spawn_pickup = true
|
|
|
|
new_chunk.position = Vector3(x * Global.chunk_size+1, 0, y * Global.chunk_size+1)
|
|
row.append(new_chunk)
|
|
$ChunkContainer.add_child(new_chunk)
|
|
|
|
if not can_spawn_pickup:
|
|
continue
|
|
|
|
if randi() % bonus_time_spawn_rate == 0:
|
|
var bonus_time = bonus_time_scn.instantiate()
|
|
bonus_time.position.x = new_chunk.position.x + randf_range(-Global.chunk_size/2, Global.chunk_size/2)
|
|
bonus_time.position.z = new_chunk.position.z + randf_range(-Global.chunk_size/2, Global.chunk_size/2)
|
|
$ChunkContainer.add_child(bonus_time)
|
|
if randi() % speed_boost_spawn_rate == 0:
|
|
var speed_boost = speed_boost_scn.instantiate()
|
|
speed_boost.position.x = new_chunk.position.x + randf_range(-Global.chunk_size/2, Global.chunk_size/2)
|
|
speed_boost.position.z = new_chunk.position.z + randf_range(-Global.chunk_size/2, Global.chunk_size/2)
|
|
$ChunkContainer.add_child(speed_boost)
|
|
|
|
chunks.append(row)
|
|
|
|
# ---- Set exits based on paths ----
|
|
for path in paths:
|
|
for i in range(path.size()):
|
|
var curr = path[i]
|
|
var next = path[i+1] if i+1 < path.size() else null
|
|
var curr_chunk = chunks[curr.x][curr.y]
|
|
var next_chunk = chunks[next.x][next.y] if next else null
|
|
|
|
if not next_chunk or next_chunk.scene_file_path != "res://chunk.tscn":
|
|
continue
|
|
|
|
var revdir = vector_to_dir(curr - next)
|
|
next_chunk.exits[revdir] = true
|
|
next_chunk.update()
|
|
|
|
if curr_chunk.scene_file_path == "res://chunk.tscn":
|
|
var dir = vector_to_dir(next - curr)
|
|
curr_chunk.exits[dir] = true
|
|
curr_chunk.update()
|
|
|
|
# ---- Open all bottom exits ----
|
|
for x in range(dimension):
|
|
var chunk = chunks[x][dimension-1]
|
|
if chunk.scene_file_path != "res://chunk.tscn":
|
|
continue
|
|
|
|
if x > 0:
|
|
chunk.exits[2] = true
|
|
if x < dimension-1:
|
|
chunk.exits[3] = true
|
|
|
|
chunk.update()
|
|
|
|
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
|
|
if Global.special_camera:
|
|
$SubViewportContainer.visible = true
|
|
else:
|
|
$SubViewportContainer.visible = false
|
|
|
|
|
|
if not $Player.is_active:
|
|
return
|
|
|
|
var spawn_radius = $Player/SpawnArea/Collision.shape.radius + randf_range(-5, 5)
|
|
var rand_unit_vec = Vector3.RIGHT.rotated(Vector3.UP, randf() * TAU)
|
|
var ped_pos = $Player.position + rand_unit_vec * spawn_radius
|
|
var final_ped_spawn_rate = int(ped_spawn_rate - floor(
|
|
ped_spawn_rate_stage_modifier * float($Player.stage_counter))
|
|
)
|
|
|
|
if (randi() % final_ped_spawn_rate == 0):
|
|
print("Pedestrian at ", ped_pos, final_ped_spawn_rate)
|
|
var new_ped = pedestrian_scn.instantiate()
|
|
add_child(new_ped)
|
|
new_ped.position = ped_pos
|
|
|
|
func _on_player_station_reached():
|
|
$SubViewportContainer.visible = false
|
|
for n in $ChunkContainer.get_children():
|
|
$ChunkContainer.remove_child(n)
|
|
n.queue_free()
|
|
initialize_world()
|