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 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) $Player.position.x = spawn.x * Global.chunk_size $Player.position.z = spawn.y * Global.chunk_size var bt = bonus_time_scn.instantiate() bt.position.x = spawn.x * Global.chunk_size - 10 bt.position.z = spawn.y * Global.chunk_size + 20 add_child(bt) var sb = speed_boost_scn.instantiate() sb.position.x = spawn.x * Global.chunk_size + 10 sb.position.z = spawn.y * Global.chunk_size + 20 add_child(sb) # ---- 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 if Vector2i(x,y) == station: new_chunk = station_scn.instantiate() elif Vector2i(x,y) == spawn: new_chunk = spawn_scn.instantiate() else: new_chunk = chunk_scn.instantiate() new_chunk.position = Vector3(x * Global.chunk_size+1, 0, y * Global.chunk_size+1) row.append(new_chunk) $ChunkContainer.add_child(new_chunk) 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 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(): for n in $ChunkContainer.get_children(): $ChunkContainer.remove_child(n) n.queue_free() initialize_world()