mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
04691aaa48
Lands the non-networked Phase 0 tasks from multiplayer-todo.md (ship/camera/ arena refactors, sim constants, background FPS handling) plus a first pass at exposing graphics/performance settings (presets, resolution scaling, vsync, FPS cap, perf overlay) and a GPU profiling harness for the real-hardware follow-up in task 0.15b.
97 lines
3.7 KiB
GDScript
97 lines
3.7 KiB
GDScript
class_name Arena
|
|
extends Node3D
|
|
|
|
# A reusable stadium: terrain, lighting, environment, two team goals, and
|
|
# spawn markers. An arena holds no rules and no state — game modes query it
|
|
# for spawn transforms and goals, then spawn ships/ball themselves.
|
|
|
|
# Per-arena Environment tuning. These live as exports (rather than baked
|
|
# into each arena's Environment sub-resource) because Godot's scene
|
|
# inheritance can only override whole top-level node properties, not
|
|
# properties nested inside a sub-resource — so each arena_0N.tscn overrides
|
|
# these on the Arena root instead, and _ready() applies them here.
|
|
@export var sky_material: Material
|
|
@export var ambient_light_color := Color(0.55, 0.6, 0.75, 1)
|
|
@export var ambient_light_energy := 0.4
|
|
@export var glow_intensity := 0.9
|
|
@export var glow_strength := 1.2
|
|
@export var glow_bloom := 0.05
|
|
@export var glow_hdr_threshold := 1.0
|
|
|
|
|
|
var _env: Environment
|
|
# Lights authored with shadow_enabled = true (the DirectionalLight3D + 4
|
|
# PitchLights omnis) — captured once, before gating ever touches them. Every
|
|
# call after the first re-applies VideoSettings.shadows_enabled to exactly
|
|
# these lights, so the set can't self-poison (if it were re-derived from
|
|
# current state, a light this same code just turned off would look
|
|
# indistinguishable from FillLight, which is authored off on purpose and must
|
|
# never be turned on by the preset ladder).
|
|
var _shadow_capable_lights: Array[Light3D] = []
|
|
|
|
|
|
func _ready():
|
|
add_to_group("arena")
|
|
# A headless server never renders, so duplicating and configuring a full
|
|
# Environment (glow/SSAO/SSIL/SDFGI) for it is pure waste — mirrors the
|
|
# same guard at ship.gd and arena_boundary.gd.
|
|
if DisplayServer.get_name() == "headless":
|
|
return
|
|
var world_env := get_node_or_null("WorldEnvironment") as WorldEnvironment
|
|
if world_env and world_env.environment:
|
|
_env = world_env.environment.duplicate(true) as Environment
|
|
world_env.environment = _env
|
|
if sky_material:
|
|
if not _env.sky:
|
|
_env.sky = Sky.new()
|
|
_env.sky.sky_material = sky_material
|
|
_env.ambient_light_color = ambient_light_color
|
|
_env.ambient_light_energy = ambient_light_energy
|
|
_env.glow_intensity = glow_intensity
|
|
_env.glow_strength = glow_strength
|
|
_env.glow_bloom = glow_bloom
|
|
_env.glow_hdr_threshold = glow_hdr_threshold
|
|
for light in find_children("*", "Light3D", true, false):
|
|
if (light as Light3D).shadow_enabled:
|
|
_shadow_capable_lights.append(light)
|
|
_apply_video_settings()
|
|
# Task 0.17: a preset change from the settings menu must take effect
|
|
# on the arena that's already loaded, not just the next one — this is
|
|
# the "settings persist and apply without a restart" acceptance bar.
|
|
VideoSettings.settings_changed.connect(_apply_video_settings)
|
|
|
|
|
|
# Re-run on every VideoSettings.settings_changed (preset or individual
|
|
# toggle) as well as once at load. Shadow gating lives here rather than in
|
|
# VideoSettings.apply_to_environment() because it targets Light3D nodes in
|
|
# this arena's own tree, not the Environment resource.
|
|
func _apply_video_settings() -> void:
|
|
if not is_instance_valid(_env):
|
|
return
|
|
VideoSettings.apply_to_environment(_env)
|
|
for light in _shadow_capable_lights:
|
|
if is_instance_valid(light):
|
|
light.shadow_enabled = VideoSettings.shadows_enabled
|
|
|
|
|
|
func get_ball_spawn() -> Transform3D:
|
|
return $BallSpawn.global_transform
|
|
|
|
|
|
func get_ship_spawns(team: int) -> Array[Transform3D]:
|
|
var spawns: Array[Transform3D] = []
|
|
var container := get_node_or_null("SpawnsTeam%d" % team)
|
|
if container:
|
|
for child in container.get_children():
|
|
if child is Marker3D:
|
|
spawns.append(child.global_transform)
|
|
return spawns
|
|
|
|
|
|
func get_goals() -> Array[Goal]:
|
|
var goals: Array[Goal] = []
|
|
for child in get_children():
|
|
if child is Goal:
|
|
goals.append(child)
|
|
return goals
|