Adds: - bomb_objective.gd: server-authoritative bomb state machine (IDLE→PLANTED→EXPLODED/DEFUSED) - plant_bomb(player_id, pos) - T-side only, LIVE phase, inside bombsite - defuse_bomb(player_id) - CT-side only, near bomb, 5s timer - cancel_defuse(player_id) - if defuser moves or is killed - bomb_explode() - configurable radius damage (10m lethal, 15m half) - 40s bomb timer (configurable) - Full query API: is_bomb_planted(), get_bomb_position(), get_bomb_timer() - Signals: bomb_planted, bomb_defused, bomb_exploded, defuse_started, defuse_cancelled - Connected to RoundManager via GameServer for round-end outcomes - bomb_carrier.gd: client-side bomb carrier UI - Bomb status/defuse progress UI updates - Direction indicator to planted bomb - "Hold E to defuse" prompt for CT near bomb - "Hold E to plant" prompt for T with bomb in bombsite - test_range.tscn: BombsiteA (south-west, -20,0,-5) and BombsiteB (north-east, 15,0,20) - 8x4m Area3D zones with BoxShape3D collision - PlantPosition Marker3D children - Group 'bomb_sites' for server discovery - game_server.gd: BombObjective integration in _ready() - Creates and wires BombObjective to RoundManager, TeamManager, DamageProcessor - Registers bomb sites from scene tree - Connects bomb_exploded/bomb_defused → RoundManager.end_round() - Connects round_ended → bomb.reset()
FPS Character Controller — Tactical Shooter
First-person character controller for the tactical shooter GDExtension simulation core.
Architecture
The character controller is a GDScript bridge between Godot's input/physics system and the C++ SimulationServer (built in task t_aca0f251).
Godot Input
│
├─► FPSCharacterController._physics_process()
│ ├─ capture input (WASD, mouse, jump, sprint, crouch, shoot)
│ ├─ build EntityInput Dictionary
│ ├─ SimulationServer.apply_input(entity_id, input_dict)
│ ├─ SimulationServer.fire_weapon(entity_id) [if shoot]
│ ├─ read entity position from server → apply to CharacterBody3D
│ └─ apply mouse look (yaw on body, pitch on camera)
│
├─► FpsCamera._process()
│ ├─ view bobbing (head bob from movement)
│ ├─ weapon sway
│ └─ sprint FOV kick
│
└─► SimulationServer (C++ GDExtension)
├─ process_tick() @ 128Hz
├─ MovementComponent.update()
├─ HitDetection.process_shot()
└─→ returns serialized snapshot
Files
| File | Purpose |
|---|---|
character/fps_character_controller.gd |
Main controller (CharacterBody3D). Input capture, simulation bridge, walk toggle, crouch, jump. |
character/fps_camera.gd |
First-person camera (Camera3D). Mouse look pitch, view bobbing, weapon sway, FOV kick. |
input/input_handler.gd |
Documentation of expected input map bindings. |
Scene Setup
-
Create a CharacterBody3D scene:
FPSCharacterController (CharacterBody3D) — script: fps_character_controller.gd ├── FpsCamera (Camera3D) — script: fps_camera.gd ├── CollisionShape3D (CapsuleShape3D) — radius: 0.4, height: 1.8 └── Weapon (Node3D, optional) — child of camera, rest transform tracked -
Configure exports on the root node:
mouse_sensitivity: 0.1–0.2 for typical tactical feelwalk_toggle:true= tap Shift to toggle sprint (CS-style)crouch_toggle:false= hold Ctrl to crouch (tactical-style)eye_height_stand/eye_height_crouch: match your capsule height
-
Set up Input Map in Project Settings:
move_forward→ Wmove_backward→ Smove_left→ Amove_right→ Djump→ Spacesprint→ Shiftcrouch→ Ctrlshoot→ Mouse LMBaim→ Mouse RMBui_cancel→ Esc (built-in, releases mouse capture)
-
Integration with SimulationServer:
# In your game manager (autoload or scene root): var server = SimulationServer.new() server.set_tick_rate(128) server.start() var player_id = server.spawn_entity(Vector3(0, 0, 0)) # When player scene is ready: $Player/FPSCharacterController.set_entity_id(player_id) # In _process(delta): while server.can_tick(delta): var snapshot = server.tick() # send snapshot to network layer
Walk Toggle Behavior
walk_toggle = true(default): Press Shift to toggle between walk and sprint. Press again to toggle off. Same feel as Counter-Strike's walk/run toggle.walk_toggle = false: Hold Shift for temporary sprint. Release to decelerate to walk speed.
Crouch Behavior
crouch_toggle = false(default): Hold Ctrl to crouch. Release to stand up.crouch_toggle = true: Press Ctrl once to crouch, press again to stand.
Crouch transitions are smooth lerps over crouch_transition_time seconds. The collision capsule height and camera eye height both lerp simultaneously.
Standalone Mode
When no SimulationServer singleton is available, the controller falls back to built-in CharacterBody3D movement (_move_local()). This is useful for:
- Testing the character controller in isolation
- Prototyping without the GDExtension compiled
- Single-player scenarios without the full simulation stack
Configure local_walk_speed, local_sprint_speed, local_crouch_speed, etc. for standalone tuning.
Dependencies
- Godot 4.2+ (for
Input.get_vector,move_toward,StringNameliterals) - SimulationServer GDExtension (from task
t_aca0f251) for server-authoritative mode - Input map actions listed above
See Also
- Scaffold:
t_aca0f251—src/movement_component.h(C++ Quake/CS movement physics) - Greybox map:
t_p1_greybox— test environment for this controller - Hitscan weapon:
t_p1_hitscan— weapon firing + lag compensation - Client prediction:
t_p1_pred— client-side prediction + server reconciliation - Round system:
t_p1_round— win/loss/sudden death on top of character controller