mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-12 04:53:44 +00:00
refactor(*): Restructure game into reusable Arena/GameMode architecture with controller-driven ships, adding Free Play and Match modes
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
class_name GameMode
|
||||
extends Node3D
|
||||
|
||||
# Base for game modes (Free Play, Match; later Vs-AI and multiplayer).
|
||||
# A mode's scene contains an Arena (the stadium) and a HUD; the mode itself
|
||||
# spawns the ball, ships, controllers, and camera in code — variable ship
|
||||
# counts with mixed controller types (player/AI/network) is exactly what
|
||||
# future modes need. Subclasses override _start() and _on_goal_scored().
|
||||
|
||||
@export var ship_scene: PackedScene = preload("res://objects/ship.tscn")
|
||||
@export var ball_scene: PackedScene = preload("res://objects/ball.tscn")
|
||||
|
||||
const CAMERA_RIG_SCENE = preload("res://scenes/ship_camera_rig.tscn")
|
||||
const MAIN_MENU_SCENE_PATH = "res://scenes/main_menu.tscn"
|
||||
|
||||
var arena: Arena
|
||||
var ball: RigidBody3D
|
||||
var ships: Array[Ship] = []
|
||||
var _ship_spawn_transforms := {}
|
||||
|
||||
|
||||
func _ready():
|
||||
# Group lets the HUD discover the game mode for timer/score signals
|
||||
add_to_group("game")
|
||||
for child in get_children():
|
||||
if child is Arena:
|
||||
arena = child
|
||||
break
|
||||
if not arena:
|
||||
push_error("GameMode: scene has no Arena child")
|
||||
return
|
||||
for goal in arena.get_goals():
|
||||
goal.goal_scored.connect(_handle_goal_scored)
|
||||
_start()
|
||||
|
||||
|
||||
# Virtual: subclasses spawn their ball/ships/camera here.
|
||||
func _start() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Virtual: the ball entered the goal owned (conceded) by `_conceding_team`.
|
||||
func _on_goal_scored(_conceding_team: int) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Debounce: a fast ball can re-trigger the goal area before the deferred
|
||||
# reset teleports it away, which would double-count the goal.
|
||||
var _goal_cooldown := false
|
||||
|
||||
func _handle_goal_scored(conceding_team: int) -> void:
|
||||
if _goal_cooldown:
|
||||
return
|
||||
_goal_cooldown = true
|
||||
get_tree().create_timer(0.5).timeout.connect(func(): _goal_cooldown = false)
|
||||
_on_goal_scored(conceding_team)
|
||||
|
||||
|
||||
func spawn_ball() -> RigidBody3D:
|
||||
ball = ball_scene.instantiate()
|
||||
add_child(ball)
|
||||
ball.global_transform = arena.get_ball_spawn()
|
||||
return ball
|
||||
|
||||
|
||||
func spawn_ship(team: int, spawn_index: int = 0, controller: ShipController = null) -> Ship:
|
||||
var ship: Ship = ship_scene.instantiate()
|
||||
ship.name = "ShipTeam%d_%d" % [team, ships.size()]
|
||||
add_child(ship)
|
||||
var spawns := arena.get_ship_spawns(team)
|
||||
var spawn_transform := spawns[spawn_index] if spawn_index < spawns.size() else Transform3D.IDENTITY
|
||||
ship.global_transform = spawn_transform
|
||||
ship.team = team
|
||||
if controller:
|
||||
ship.set_controller(controller)
|
||||
ships.append(ship)
|
||||
_ship_spawn_transforms[ship] = spawn_transform
|
||||
return ship
|
||||
|
||||
|
||||
func spawn_camera_rig(target: Ship) -> ShipCameraRig:
|
||||
var rig: ShipCameraRig = CAMERA_RIG_SCENE.instantiate()
|
||||
add_child(rig)
|
||||
rig.target = target
|
||||
return rig
|
||||
|
||||
|
||||
func reset_ball() -> void:
|
||||
if is_instance_valid(ball):
|
||||
_reset_body(ball, arena.get_ball_spawn())
|
||||
|
||||
|
||||
func reset_ships() -> void:
|
||||
for ship in ships:
|
||||
if is_instance_valid(ship):
|
||||
_reset_body(ship, _ship_spawn_transforms[ship])
|
||||
|
||||
|
||||
func _reset_body(body: RigidBody3D, to: Transform3D) -> void:
|
||||
# Deferred: a RigidBody3D transform can't be set mid-physics-step
|
||||
body.set_deferred("global_transform", to)
|
||||
body.set_deferred("linear_velocity", Vector3.ZERO)
|
||||
body.set_deferred("angular_velocity", Vector3.ZERO)
|
||||
|
||||
|
||||
func _unhandled_input(event):
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
get_tree().change_scene_to_file(MAIN_MENU_SCENE_PATH)
|
||||
Reference in New Issue
Block a user