mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
fix(game-mode): recover ships/ball that escape through an open goal in every mode
The goal mouths are now a real navigable hole in the end walls, sized to the ball rather than the ship — a ship's 1x1 cross-section fits through it, and there's nothing behind the net to stop it. The escape failsafe previously only existed in TrainingMode (where a physics regression just wastes training time); now that any ship can genuinely fly out through an open goal, every mode needs it or a stray ship/ball falls into the void with no way back short of quitting. Moved up to GameMode as the shared default _physics_process, removing TrainingMode's now-duplicate copy.
This commit is contained in:
@@ -175,3 +175,37 @@ func _reset_body(body: RigidBody3D, to: Transform3D) -> void:
|
||||
func _unhandled_input(event):
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
get_tree().change_scene_to_file(ScenePaths.MAIN_MENU)
|
||||
|
||||
|
||||
# Escape failsafe. The arena is meant to be fully enclosed, but the goal
|
||||
# mouths are now a real navigable hole in the end walls (see
|
||||
# ArenaBoundary._build_end_wall_colliders) sized to the ball, not the ship —
|
||||
# a ship's 1x1 cross-section fits through it, and there is nothing behind the
|
||||
# net to stop it. Previously this only existed in TrainingMode (a physics
|
||||
# regression there just wastes training time); now that any ship can
|
||||
# genuinely fly out through an open goal, every mode needs it, or a stray
|
||||
# ship/ball falls into the void with no way back short of quitting. Runs by
|
||||
# default every tick; TrainingMode overrides _physics_process entirely and
|
||||
# calls this itself alongside its own episode logic.
|
||||
const ESCAPE_MARGIN := 15.0
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
_respawn_escaped_bodies()
|
||||
|
||||
|
||||
func _respawn_escaped_bodies() -> void:
|
||||
for ship in ships:
|
||||
if is_instance_valid(ship) and _is_escaped(ship.global_position):
|
||||
push_warning("GameMode: ship escaped the enclosed arena — check boundary colliders")
|
||||
_reset_body(ship, _ship_spawn_transforms[ship])
|
||||
if is_instance_valid(ball) and _is_escaped(ball.global_position):
|
||||
push_warning("GameMode: ball escaped the enclosed arena — check boundary colliders")
|
||||
_reset_body(ball, arena.get_ball_spawn())
|
||||
|
||||
|
||||
func _is_escaped(position: Vector3) -> bool:
|
||||
return absf(position.x) > ArenaBoundary.INNER_HALF_X + ESCAPE_MARGIN \
|
||||
or absf(position.z) > ArenaBoundary.INNER_HALF_Z + ESCAPE_MARGIN \
|
||||
or position.y < -ESCAPE_MARGIN \
|
||||
or position.y > ArenaBoundary.INNER_HEIGHT + ESCAPE_MARGIN
|
||||
|
||||
@@ -90,12 +90,6 @@ const MAX_RANDOM_SHIP_SPEED := 8.0
|
||||
# Sim runs at 60 physics ticks per sim-second regardless of speedup.
|
||||
const TICKS_PER_SIM_SECOND := 60.0
|
||||
|
||||
# The arena is physically enclosed, so nothing should ever get this far out.
|
||||
# If a body escapes anyway (physics regression, boundary edit), it is warned
|
||||
# about and respawned with no reward change and no episode end — a multi-hour
|
||||
# training run must survive it, and the escape must not shape rewards.
|
||||
const ESCAPE_MARGIN := 15.0
|
||||
|
||||
var _agents: Array[ShipAIController] = []
|
||||
|
||||
# Eval mode state (see header comment)
|
||||
@@ -320,24 +314,6 @@ func _physics_process(_delta):
|
||||
return
|
||||
|
||||
|
||||
# Escape failsafe: see ESCAPE_MARGIN.
|
||||
func _respawn_escaped_bodies() -> void:
|
||||
for ship in ships:
|
||||
if is_instance_valid(ship) and _escaped(ship.global_position):
|
||||
push_warning("TrainingMode: ship escaped the enclosed arena — check boundary colliders")
|
||||
_place_body(ship, _ship_spawn_transforms[ship], Vector3.ZERO, Vector3.ZERO)
|
||||
if is_instance_valid(ball) and _escaped(ball.global_position):
|
||||
push_warning("TrainingMode: ball escaped the enclosed arena — check boundary colliders")
|
||||
_place_body(ball, arena.get_ball_spawn(), Vector3.ZERO, Vector3.ZERO)
|
||||
|
||||
|
||||
func _escaped(position: Vector3) -> bool:
|
||||
return absf(position.x) > ArenaBoundary.INNER_HALF_X + ESCAPE_MARGIN \
|
||||
or absf(position.z) > ArenaBoundary.INNER_HALF_Z + ESCAPE_MARGIN \
|
||||
or position.y < -ESCAPE_MARGIN \
|
||||
or position.y > ArenaBoundary.INNER_HEIGHT + ESCAPE_MARGIN
|
||||
|
||||
|
||||
func _on_goal_scored(conceding_team: int) -> void:
|
||||
if _eval:
|
||||
_eval_goals[1 - conceding_team] += 1
|
||||
|
||||
Reference in New Issue
Block a user