Files
tactical-shooter/client/assets/scenes/modular/art_kit.gd
T
shawn f20d532add t_p1_round: Add round/match lifecycle system (RoundManager)
- New server/scripts/round/round_manager.gd with:
  - RoundPhase enum (WARMUP, PREP, LIVE, POST)
  - Round life cycle: warmup -> prep (15s) -> live (105s) -> post (8s)
  - Team elimination detection using entity->peer->team mappings
  - Time-expired round ending (default: CT wins)
  - Economy hooks: win money (250), loss money (900 escalating to 900), kill bonus (00)
  - All requested signals: round_phase_changed, round_started, round_ended, match_point, warmup_ended, round_economy_data

- Updated GameServer:
  - Creates and wires RoundManager in _ready()
  - Connects DamageProcessor.player_killed -> round tracking + elimination detection
  - Added peer_to_entity mapping for backwards entity lookup
  - Added RCON command handler (_on_rcon_command) for start_match/end_round

- Updated RCON command handler:
  - Added start_match and end_round to dispatch list and help text
2026-07-01 20:30:01 -04:00

161 lines
4.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tool
extends Node
# =============================================================================
# art_kit.gd — Modular Art Kit Factory
# =============================================================================
# Provides factory methods for spawning modular environment pieces and a
# preview catalogue for map builders.
#
# Each piece is a standalone .tscn under pieces/ with:
# - CSG shapes (Box3D, Cylinder3D, Polygon3D, Combiner3D)
# - Solid-color StandardMaterial3D placeholders (roughness 0.8, metallic 0.0)
# - Collision enabled (use_collision = true or StaticBody3D + CollisionShape3D)
# - < 200 tris each
#
# Usage:
# var piece = ArtKit.spawn_piece("crate", parent_node)
# var cat = ArtKit.preview_catalogue()
# var stack = ArtKit.random_crate_layout(3, 2) # returns parent Node3D
# =============================================================================
const PIECES_DIR := "res://client/assets/scenes/modular/pieces/"
# Catalogue definition: name, description, filename (without .tscn)
const CATALOGUE: Array[Dictionary] = [
{
"name": "floor_tile",
"description": "2×2×0.2m concrete slab — grey",
"file": "floor_tile"
},
{
"name": "wall_segment",
"description": "4×0.2×3m wall panel — blue-grey",
"file": "wall_segment"
},
{
"name": "corner_wall",
"description": "L-shaped corner piece (2m each leg) — blue-grey",
"file": "corner_wall"
},
{
"name": "pillar",
"description": "0.3×3×0.3m column — dark grey",
"file": "pillar"
},
{
"name": "crate",
"description": "1×1×1m storage box — brown/tan",
"file": "crate"
},
{
"name": "barrel",
"description": "0.6m radius × 1m tall cylinder — red",
"file": "barrel"
},
{
"name": "cover_wall",
"description": "2×0.2×1.3m low wall (crouch-height cover) — grey",
"file": "cover_wall"
},
{
"name": "ramp",
"description": "2×2m, 30° incline ramp — yellow",
"file": "ramp"
},
{
"name": "stairs",
"description": "3-step staircase (2m wide, 0.25m rise per step) — grey",
"file": "stairs"
},
{
"name": "window_frame",
"description": "4×3m wall with 2×2m opening — blue-grey",
"file": "window_frame"
}
]
# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------
## Spawns a piece by name, adds it as a child of `parent`, and returns it.
## Returns null if the piece name is unknown or the scene fails to load.
static func spawn_piece(piece_name: String, parent: Node) -> Node3D:
var entry := _find_entry(piece_name)
if entry.is_empty():
push_error("ArtKit: Unknown piece '%s'. Use preview_catalogue() to see available pieces." % piece_name)
return null
var path := PIECES_DIR + entry.file + ".tscn"
var scene := load(path)
if scene == null:
push_error("ArtKit: Failed to load scene at '%s'." % path)
return null
var instance: Node3D = scene.instantiate()
parent.add_child(instance)
instance.owner = parent if not Engine.is_editor_hint() else _get_editor_owner(parent)
return instance
## Returns an array of { name, description, path } for all available pieces.
static func preview_catalogue() -> Array[Dictionary]:
var result: Array[Dictionary] = []
for entry in CATALOGUE:
result.append({
"name": entry.name,
"description": entry.description,
"path": PIECES_DIR + entry.file + ".tscn"
})
return result
## Generates a random stack of crates for quick testing layouts.
## `width` — number of crates along X (default 3)
## `height` — number of crates stacked vertically (default 2)
## Returns a Node3D containing the crate instances.
static func random_crate_layout(width: int = 3, height: int = 2) -> Node3D:
var root := Node3D.new()
root.name = "CrateLayout_%dx%d" % [width, height]
var crate_scene := load(PIECES_DIR + "crate.tscn")
if crate_scene == null:
push_error("ArtKit: Could not load crate scene.")
return root
for y in range(height):
for x in range(width):
# Add slight random offset for natural look (±0.05m)
var offset_x := randf_range(-0.05, 0.05)
var offset_z := randf_range(-0.05, 0.05)
# Full rotation in 90° increments
var rot_y := randi() % 4 * 90.0
var crate: Node3D = crate_scene.instantiate()
crate.position = Vector3(x * 1.0 + offset_x, y * 1.0 + 0.5, offset_z)
crate.rotation_degrees = Vector3(0, rot_y, 0)
root.add_child(crate)
return root
# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------
static func _find_entry(piece_name: String) -> Dictionary:
for entry in CATALOGUE:
if entry.name == piece_name:
return entry
return {}
## Returns the editor's top-level owner so new nodes are treated as part of the scene.
static func _get_editor_owner(node: Node) -> Node:
var owner = node
while owner and owner.owner:
owner = owner.owner
return owner