Files
CosmicClash/Game/scripts/arena_registry.gd
T
2026-08-21 18:38:30 +01:00

50 lines
2.5 KiB
GDScript

class_name ArenaRegistry
# Single source of truth for available arenas: the Free Play menu dropdown
# lists all of these, and Match/Spectate pick at random each session from
# those with "random" true. Training is exempt — training.tscn/
# training_elevated.tscn each keep their own fixed Arena child.
#
# Elevated-goal variants are Free-Play-only ("random": false) until a
# checkpoint trained on training_elevated.tscn is promoted — the current
# promoted bots (see main_menu.gd's DIFFICULTIES) were trained exclusively
# on floor-level goals and can't be expected to score on an elevated one.
# Flip a variant's "random" flag to true once that training/promotion has
# happened.
const ARENAS := [
{"name": "Starfield (Floor Goals)", "path": "res://scenes/arena_01.tscn", "random": true},
{"name": "Starfield (Elevated Goals)", "path": "res://scenes/arena_01_elevated.tscn", "random": false},
{"name": "Nebula (Floor Goals)", "path": "res://scenes/arena_02.tscn", "random": true},
{"name": "Nebula (Elevated Goals)", "path": "res://scenes/arena_02_elevated.tscn", "random": false},
{"name": "Asteroid Field (Floor Goals)", "path": "res://scenes/arena_03.tscn", "random": true},
{"name": "Asteroid Field (Elevated Goals)", "path": "res://scenes/arena_03_elevated.tscn", "random": false},
]
static func random_path() -> String:
var candidates := ARENAS.filter(func(arena): return arena["random"])
return candidates[randi() % candidates.size()]["path"]
# The arenas a server may rotate through, in declaration order. Same filter as
# random_path(): an elevated-goal variant is Free-Play-only until a checkpoint
# trained on it is promoted, and a dedicated server rotating onto one would
# hand every bot-filled slot an arena it cannot score in.
static func rotation_paths() -> Array:
return ARENAS.filter(func(arena): return arena["random"]).map(func(arena): return arena["path"])
# Task 6.5's arena rotation, as pure arithmetic so it is unit-testable without
# a server: given how many matches have already been played, which arena is
# next. `random` deliberately still uses the global RNG (the caller wants
# variety, not reproducibility); `sequential` is a pure function of the count,
# which is what makes "the server cycles arenas" an assertable claim rather
# than an observation about luck.
static func path_for_match(match_index: int, mode: String) -> String:
var paths := rotation_paths()
if paths.is_empty():
return ARENAS[0]["path"]
if mode == "random":
return paths[randi() % paths.size()]
return paths[posmod(match_index, paths.size())]