group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
Game Class Reference

Top-level client game object. More...

#include <Game.hpp>

Inheritance diagram for Game:
[legend]
Collaboration diagram for Game:
[legend]

Classes

struct  PredictedPlayerState
struct  ReconciliationDecision
struct  BeamAudioState
struct  PlayerSfxState
struct  PendingDamageNumber
struct  HandMountDebugTarget
struct  GripSwapState
 Per-entity weapon-swap fade state. More...
struct  TransientVfxLight

Public Member Functions

bool initDebugUI (const AppContext &ctx)
 Create the Game-owned ImGui context before App initialises the renderer backend.
bool init (AppContext &ctx)
 Initialise all subsystems and spawn the local player entity.
SDL_AppResult event (SDL_Event *event) override
 Forward an SDL event to ImGui and handle application-level keys.
SDL_AppResult iterate () override
 Advance one frame: sample input, step physics, render.
bool shouldReturnToLobby () const
 True once the server has returned the match phase to the lobby.
std::optional< PostMatchResultconsumePostMatchResult ()
 Return the final scoreboard snapshot captured before the server reset match stats.
bool consumeReturnToMainMenu ()
 True if the user requested leaving the match for the main menu, then clear that request.
bool consumeServerShutdownNotice ()
 True if the main-menu return was caused by the server connection closing, then clear that reason.
void quit () override
 Shut down all subsystems in reverse-init order.
void shutdownAfterRenderer () override
 Destroy the Game-owned ImGui context after App shuts down the renderer backend.
void refreshRemotePlayerRenderables ()
 Update Renderable components for remote players (model, scale, orientation from animation rig).
void refreshRemoteProjectileRenderables ()
 Assign Renderable components to newly spawned projectile entities.
void refreshRemoteRespawnRenderables ()
 Reset Renderable visibility for players transitioning through respawn.
void refreshRemotePowerupRenderables ()
 Assign or update Renderable components for replicated powerup entities.
void refreshRemoteHealthPackRenderables ()
 Assign or update Renderable components for replicated health pack spawner entities.
void refreshDroppedWeaponRenderables ()
 Assign Renderable components to dropped-weapon entities (mirrors spawner visuals).
Public Member Functions inherited from IScreen
virtual ~IScreen ()=default

Private Types

enum class  HandMountDebugSpace : std::uint8_t { None , ThirdPerson , FirstPerson }
enum class  HandMountDebugPoint : std::uint8_t {
  Shoulder , Elbow , Palm , Finger0 ,
  Finger1 , Finger2 , Finger3 , Finger4
}

Private Member Functions

bool applyIncomingSnapshot (std::uint32_t snapshotTick, const std::uint8_t *bytes, Uint32 size, Uint64 captureNs, std::uint32_t &ackedTick)
 Deserialize a snapshot from the server and update the ECS registry.
void handleLocalPlayerReady (entt::entity local)
 Emplace player-control components onto the mapped local entity and record it.
void clearPredictedStateHistory () noexcept
void storePredictedPlayerState (std::uint32_t tick)
std::optional< PredictedPlayerStatecaptureLocalPredictedState () const
const PredictedPlayerStatepredictedStateForTick (std::uint32_t tick) const noexcept
void restoreLocalPredictedState (const PredictedPlayerState &state)
ReconciliationDecision evaluateReconciliationSkip (const PredictedPlayerState &authoritative, const PredictedPlayerState *predictedAtAck, const std::optional< PredictedPlayerState > &currentBeforeSnapshot) const noexcept
void updateCachedPostMatchResult ()
 Refresh the final post-match scoreboard snapshot while FINISHED stats are still replicated.
void openChat ()
 Enter chat input mode and release normal gameplay input capture.
void closeChat ()
 Leave chat input mode and restore normal gameplay input capture.
void submitChat ()
 Send the current chat draft to the server and close the chat box.
void appendChatMessage (ClientId sender, std::string_view message)
 Queue a replicated chat message for HUD display.
void appendLocalChatMessage (std::string_view message)
 Queue a locally-authored chat echo while waiting for server replication.
void appendPopupMessage (HudPopupKind kind, std::string_view message)
 Queue a generic transient HUD popup message.
void clearGameplayInputForChat ()
 Clear held gameplay actions so typing chat cannot leak into movement or weapons.
void applyFrameRateLimit ()
 Apply FPS-limit strategy based on limitFPSToMonitor, monitor Hz, and physics Hz.
void attachAnimatedCharacter (entt::entity e)
 Attach a fresh AnimatedCharacter component to an entity.
void onWeaponFired (const struct WeaponFiredEvent &evt)
 Dispatcher subscriber: when ANY player fires a shot, push a per-weapon-class additive pitch impulse onto that player's spine.
void adoptGamepad (SDL_JoystickID id)
 Open the gamepad with the given SDL_JoystickID and make it the active controller, but only if none is currently bound (first-device-wins policy).
void scanForConnectedGamepads ()
 Open the first already-connected gamepad, if any.
glm::vec3 muzzleFlashOrigin (const glm::vec3 &fallback) const
 World position to spawn local weapon particles.
glm::vec3 muzzleFlashLightPosition (const glm::vec3 &muzzleOrigin) const
 Offset a muzzle point to the actual transient point-light position.
void spawnMuzzleFlashLight (const glm::vec3 &pos, WeaponType weaponType)
 Spawn a transient muzzle-flash point light at pos.
void spawnExplosionFlashLight (const glm::vec3 &pos, WeaponType weaponType, float radius)
 Spawn a transient explosion point light at pos.

Private Attributes

SDL_Window * window = nullptr
 The application window.
DebugUI debugUI
 Owns the ImGui context and SDL3 input backend.
NewRendererrenderer = nullptr
 Borrowed renderer owned by App.
Registry registry
 The shared ECS registry.
Clientclient = nullptr
 Borrowed UDP network client owned by App.
SfxSystemsfxSystem = nullptr
 Borrowed audio system owned by App.
UserSettingsuserSettings = nullptr
 Borrowed user settings owned by App.
std::string_view userSettingsPath_
 Borrowed save path for user settings.
std::optional< registry_serialization::LoadersnapshotLoader_
 Incremental loader; created on first snapshot.
std::optional< entt::entity > mappedLocalPlayerEntity_
 Local-registry entity for this client's player, once assigned.
ParticleSystem particleSystem
 Client-side VFX particle system.
VoiceChatSystem voiceChat_
 Push-to-talk Opus proximity voice chat.
Hud hud_
 In-game HUD overlay system.
entt::dispatcher dispatcher
 Event bus for weapon/impact/explosion events.
Uint64 prevTime = 0
 SDL performance counter at the last iterate() call.
float accumulator = 0.0f
 Unprocessed physics time in seconds.
int tickCount = 0
 Total physics ticks elapsed since start.
uint32_t clientPredictTick = 0
 Monotonic per-tick counter stamped onto outgoing InputSnapshots.
bool prevShootingForDebug_ = false
 PR-20: tracks last frame's input.shooting so the fire-rising-edge detector inside iterate() only captures the FIRST tick of a click (a "trigger pull"), not every tick the button is held.
InputRingBuffer inputRing_
 Phase 5b: ring buffer of recent stamped inputs for replay- based reconciliation.
std::array< PredictedPlayerState, InputRingBuffer::k_capacitypredictedStateHistory_ {}
bool mouseCaptured = true
 True when relative mouse mode is active.
SDL_Gamepad * activeGamepad_ = nullptr
 Currently-bound gamepad, or nullptr if none is plugged in.
SDL_JoystickID activeGamepadId_ = 0
 SDL_JoystickID of the active gamepad — needed to identify the device on SDL_EVENT_GAMEPAD_REMOVED so we don't tear down a different controller when a second one disconnects.
BindingDevice lastInputDevice_ {}
 Last input device the player actually used, for HUD glyph selection.
float gamepadLookSensitivity = 6.0f
 Right-stick look speed in radians per second at full deflection.
systems::GamepadAimAssistConfig aimAssistCfg_
 AAA-style gamepad aim assist tuning.
systems::GamepadAimAssistState aimAssistState_
 Persistent inter-frame state for aim assist (anchor on target AABB + previous-frame snapshot used to compute the angular delta from which the rotational pull is derived).
std::unique_ptr< WorkerPoolworkerPool_
 Persistent thread pool for parallel-for over per-frame loops (currently the animation update; future: parallel frustum cull, particle update, ECS transforms).
bool localWasDead_ = false
 Local player's dead state last frame — used to detect the dead→alive (respawn) edge and snap the view to the spawn yaw.
float mouseSensitivity = user_settings::kDefaultMouseSensitivity
 Radians per pixel of mouse movement.
float horizontalFovDegrees = 90.0f
 Player-facing horizontal camera field of view in degrees.
bool renderSeparateFromPhysics = true
 Render every iterate() with interpolation (true) vs only after a physics tick (false).
bool inputSyncedWithPhysics = true
 Sample mouse once per physics tick (true) vs every iterate() call (false).
bool limitFPSToMonitor = true
 VSync on (true) / off (false).
Uint64 softLimitPeriod = 0
 Target frame period in perf-counter ticks (0 = disabled).
Uint64 softLimitNextFrame = 0
 Performance counter target for next frame deadline.
FrameRecorder recorder
 R-key toggled frame-state + screenshot recorder.
uint64_t frameCount = 0
 Monotonic render-frame counter.
glm::vec3 cachedEye_ {0.f, 100.f, 0.f}
glm::vec3 cachedCamFwd_ {0.f, 0.f, 1.f}
bool cachedGravFlipped_ {false}
 Local player gravity-flip state, updated each iterate().
float currentCameraRoll_ {0.0f}
 Smoothed camera roll angle (radians).
int localEmote_ {-1}
 Active local emote index (EmoteCatalog), or -1.
float emoteCamBlend_ {0.0f}
 0 = first-person, 1 = third-person emote cam (eased).
bool killcamActive_ {false}
 True this frame while the killcam is driving the camera.
glm::vec3 killcamEye_ {0.0f}
 Eye position locked at the moment of death.
float killcamYaw_ {0.0f}
 Smoothed killcam yaw (radians).
float killcamPitch_ {0.0f}
 Smoothed killcam pitch (radians).
glm::vec3 killcamKillerCenter_ {0.0f}
 Killer AABB center (world) for the HUD label.
glm::vec3 killcamKillerHalf_ {0.0f}
 Killer AABB half-extents (world) for the HUD label.
entt::entity killcamKillerEntity_ {entt::null}
 Killer entity (drives the chams pass), or null.
std::string killcamKillerName_
 Killer's display nickname (for the HUD label).
physics::MapCollisionData mapCollision_
AssetRegistry assets_
int weaponModelIndices_ [kRenderableWeaponTypeCount] = {-1, -1, -1, -1, -1}
int weaponAssetIds_ [kRenderableWeaponTypeCount] = {-1, -1, -1, -1, -1}
int viewmodelLeftHandModelIdx_ = -1
int viewmodelRightHandModelIdx_ = -1
int handMountDebugMarkerModelIdx_ = -1
int rocketProjectileModelIdx_ = -1
int grenadeModelIdx_ = -1
int heGrenadeModelIdx_ = -1
int stickyGrenadeModelIdx_ = -1
int molotovModelIdx_ = -1
int medkitModelIdx_ = -1
int shieldPowerupModelIdx_ = -1
int damagePowerupModelIdx_ = -1
bool showDynLightUI_ = false
 Show the Dynamic Lighting panel.
bool showHdrDebugUI_ = false
 Show the HDR / tonemap debug panel.
bool showHudDebug_ = false
 Show the HUD Tweaker panel.
bool showMenuThemeUI_ = false
 Show the Menu Theme Tweaker panel.
bool flashlightEnabled_ = false
 Point light at camera position.
float flashlightIntensity_ = 8.0f
 Flashlight brightness.
float flashlightRange_ = 800.0f
 Flashlight attenuation range.
float flashlightOffset_ = 30.0f
 Forward offset from eye.
bool movableSphereEnabled_ = false
 Glow sphere following the player.
float sphereFollowDist_ = 150.0f
 Distance ahead of player.
float sphereIntensity_ = 5.0f
 Point light intensity of movable sphere.
float sphereRange_ = 500.0f
 Point light range of movable sphere.
bool beamEnabled_ = false
 Show the bloom beam.
glm::vec3 beamStartOff_ {30.0f, -5.0f, 10.0f}
 Beam start offset (fwd, up, right) from eye.
glm::vec3 beamEndOff_ {200.0f, -5.0f, 10.0f}
 Beam end offset (fwd, up, right) from eye.
float beamRadius_ = 3.0f
 Beam cylinder radius.
glm::vec3 beamColor_ {0.6f, 0.1f, 1.0f}
 Beam emissive colour (purple).
float beamLightIntensity_ = 4.0f
 Point light intensity per sample.
float beamLightRange_ = 300.0f
 Point light range per sample.
float beamLightSpacing_ = 60.0f
 Distance between point lights along beam.
WeaponType currentEquippedType_ = WeaponType::Rifle
 Cached each frame.
WeaponType lastEquippedType_ = WeaponType::Rifle
 Previous frame's weapon — triggers default reload on change.
bool viewmodelDefaultsApplied_ = false
bool wasChargingRailgun_ = false
 True last frame if local player was charging RailGun.
std::unordered_map< entt::entity, BeamAudioStatebeamAudioStates_
std::unordered_map< entt::entity, std::array< float, 5 > > footstepPhases_
std::unordered_map< entt::entity, float > footstepCooldowns_
std::unordered_map< entt::entity, PlayerSfxStateplayerSfxState_
float hitmarkerTimer_ = 0.0f
 Remaining display time (fades out over this).
bool hitmarkerIsHeadshot_ = false
 True when the current hitmarker was a headshot.
bool hitmarkerShieldBreak_ = false
 True when the current hit depleted target armor.
std::vector< PendingDamageNumberpendingDamageNumbers_
entt::entity accumTarget_ = entt::null
 Current target being damaged.
int accumTotal_ = 0
 Running damage total.
float accumResetTimer_ = 0.f
 Timer to reset accumulator after inactivity.
uint8_t accumLastHitType_ = 0
 0=health(white), 1=shield(blue), 2=headshot(gold).
HudShotgunBlast shotgunPelletAccum_ {}
 In-flight accumulator (resets when 9 pellets received).
int shotgunPelletAccumCount_ = 0
 Pellets received for the current in-flight blast.
float shotgunPelletLastTimeSec_ = 0.f
 Game-time of the last pellet (for stale-reset).
HudShotgunBlast lastShotgunBlast_ {}
 Most recently completed blast (staged for HUD).
float prevHealth_ = 100.f
float prevArmor_ = 100.f
float matchElapsedSeconds_ = 0.f
 Time accumulated while the match is in PLAYING phase (s).
int prevPrimaryWeaponType_ = -1
 Last-frame snapshot of the local player's weapon-slot types (-1 = empty).
int prevSecondaryWeaponType_ = -1
int prevAmmoReserve_ = -1
 Drives "+N <weapon> AMMO" reserve-grow notifications.
std::vector< HudPickupNotificationpendingPickupNotifications_
 Pickup notifications queued for the next HUD frame.
std::vector< HudPopupMessagependingPopupMessages_
 Generic popup notifications queued for the next HUD frame.
std::vector< HudChatMessagechatMessages_
std::vector< HudVoiceSpeakervoiceSpeakers_
std::string chatDraft_
std::deque< std::string > pendingLocalChatEchoes_
bool chatOpen_ = false
float vmScale = 1.0f
 Weapon model scale (model is in mm).
float vmForward = 0.0f
 Forward offset from eye (Quake units).
float vmRight = 0.0f
 Right offset from eye.
float vmDown = 0.0f
 Downward offset from eye.
float vmYawOffset = 0.0f
 Extra yaw (degrees) applied to the model before camera orient.
float vmPitchOffset = 0.0f
 Extra pitch (degrees).
float vmRollOffset = 0.0f
 Extra roll (degrees).
bool showViewmodelUI = false
 Show the Viewmodel Tweaker window.
float prevSwayYaw_ = 0.0f
float prevSwayPitch_ = 0.0f
float swayOffsetX_ = 0.0f
 Current horizontal sway (right axis, Quake units).
float swayOffsetY_ = 0.0f
 Current vertical sway (up axis).
bool swayInitialized_ = false
 Guard against initial delta spike.
float swayAmplitudeYaw_ = 3.0f
float swayAmplitudePitch_ = 2.0f
float swayDecayRate_ = 8.0f
 Exponential decay speed.
float swaySmoothing_ = 0.15f
 Input smoothing (0..1, lower = smoother).
float recoilPitch_ = 0.0f
 Current recoil pitch offset (degrees).
float recoilPushBack_ = 0.0f
 Current recoil backward offset (Quake units).
float recoilRoll_ = 0.0f
 Current recoil roll offset (degrees).
bool useSpringCameraRecoil_ = true
 Toggle Apex-spring path vs legacy instant snap-pitch write.
float cameraRecoilPitch_ = 0.0f
 Spring current offset (radians; negative = looking up).
float cameraRecoilYaw_ = 0.0f
float cameraRecoilPitchVel_ = 0.0f
 Spring velocity (rad/s).
float cameraRecoilYawVel_ = 0.0f
float cameraRecoilTargetPitch_ = 0.0f
 Spring target; accumulates per shot, decays only when idle.
float cameraRecoilTargetYaw_ = 0.0f
float committedRecoilPitch_ = 0.0f
 Portion of cameraRecoilPitch_ already added to snap.pitch.
float committedRecoilYaw_ = 0.0f
 Each frame we commit (current - committed) and update.
float recoilPatternScaleMultiplier_ = 2.0f
 Live multiplier on WeaponConfig.recoilPatternScale.
float cameraRecoilOmega_ = 35.0f
 Spring angular frequency (rad/s). Higher = snappier kick.
float cameraRecoilTargetDecay_ = 5.0f
 Idle-recovery decay rate (1/s). Higher = faster snap-back.
float cameraRecoilIdleThreshold_ = 0.18f
 Seconds off-trigger before target starts decaying.
bool useRecoilCompensation_ = false
 false = Approach A (no restore — aim keeps the view angles the recoil walked it to; player compensates manually); true = Approach B.
float lastSnapPitchAfterCommit_ = 0.0f
 snap.pitch saved at end of last spring tick — diff against
float lastSnapYawAfterCommit_ = 0.0f
 current to recover the player's mouse delta this frame.
bool haveLastSnap_ = false
 Becomes true after the first frame the local player exists.
float compensationCreditPitch_ = 0.0f
float compensationCreditYaw_ = 0.0f
float compensationCreditCap_ = 0.10f
 Max banked credit (rad). ~5.7° per axis.
float compensationCreditDecay_ = 1.0f
 Credit decay rate (1/s); stale credit fades.
float reloadDownwardOffset_ = 0.0f
 Downward offset for the reload animation.
float grenadeThrowDownwardOffset_ = 0.0f
 Downward offset for the grenade-throw animation.
float localFireCooldown_ = 0.0f
 Countdown timer; fire VFX only when <= 0.
float localRecoilHeat_ = 0.0f
float recoilIdleTime_ = 0.0f
int pendingScrollSwitch_ = 0
 +1 = next slot, -1 = previous slot, consumed each frame.
ThirdPersonWeaponParams tpWeaponParams_ [kRenderableWeaponTypeCount]
 Runtime-tunable procedural/scale params.
int tpTuneWeaponIdx_ = 0
 Which weapon type the Weapon Hold tweaker is editing.
bool tpFreezeAnimations_ = false
 Debug: freeze every animator's playback while tuning.
std::array< WeaponHoldPose, kRenderableWeaponTypeCountweaponHoldPoses_ {}
 Runtime, live-tuned.
std::array< WeaponHoldPose, kRenderableWeaponTypeCountauthoredWeaponHoldPoses_ {}
 Compile-time defaults.
std::array< std::string, kRenderableWeaponTypeCountweaponHoldPosePaths_ {}
 Source TOML path each.
std::array< std::filesystem::file_time_type, kRenderableWeaponTypeCountweaponHoldPoseMTimes_ {}
float holdPoseReloadAccumulator_ = 0.0f
 Seconds since last hot-reload mtime poll (~4 Hz throttle).
float holdRotStepDeg_ = 5.0f
 Delta-rotation step (deg) for the spine-rotation buttons.
bool holdShowDebugMarker_ = true
 Render the spine-anchor axis marker when the tweaker is open.
bool showWeaponHoldUI_ = false
 Show the Weapon Hold tweaker window.
FirstPersonHandMountParams fpHandMountParams_ [kRenderableWeaponTypeCount]
 Runtime-tunable copy; initialised from defaults.
FirstPersonHandMountParams authoredFPHandMountParams_ [kRenderableWeaponTypeCount]
 Defaults loaded from authored weapon assets.
int fpHandMountTuneWeaponIdx_ = 0
 Which weapon type is being tuned.
bool showFPHandMountUI_ = false
 Show the FP Arm Tweaker window.
HandMountDebugTarget handMountDebugTarget_ {}
WeaponSpawnerModelParams spawnerWeaponParams_ [kRenderableWeaponTypeCount]
 Runtime-tunable copy; initialised from spawner defaults.
int spawnerTuneWeaponIdx_ = 0
 Which weapon type is being tuned.
bool showWeaponSpawnerModelUI_ = false
 Show the Weapon Spawner Model Tweaker window.
CharacterRig charRig_
 Shared skinned rig (skeleton + bind pose + weights).
int rightHandJointIdx_ = -1
 Cached "mixamorig:RightHand" joint index (-1 = not present).
int spine2JointIdx_ = -1
 Cached "mixamorig:Spine2" joint index.
std::unordered_map< entt::entity, GripSwapStategripSwapState_ {}
float aimAssistParityAccumSec_ = 0.0f
 Seconds since the last aim-assist parity check log line (Phase F).
AnimationLibrary animLibrary_
 Collection of ozz clips on the shared rig.
CpuLbsSkinningBackend skinBackend_
 Phase-1 CPU linear-blend-skinning backend.
AnimationTesterState animUI_
 Persistent state for the Animation Tester panel.
HitboxRig clientHitboxRig_
 Hitbox definitions for client-side debug visualization.
float kRigScale_ = 1.0f
 Per-renderable scale for animated characters (auto-calculated, tunable).
float kRigVerticalOffset_
 Per-renderable Y translation for animated characters (auto-calculated, tunable).
float rigMeshMinY_ = 0.0f
 Minimum Y of the bind-pose mesh vertices (model space).
std::unordered_set< entt::entity > dissolveSpawned_
 Entities for which a death-dissolve particle burst has already been spawned (so we emit it exactly once per death).
float fpsHistory [k_fpsHistorySize] = {}
 Circular buffer of per-frame FPS samples.
int fpsHistoryHead = 0
 Next write index.
int fpsHistoryCount = 0
 Valid sample count (saturates at k_fpsHistorySize).
Uint64 prevRenderTime = 0
 Perf counter at the last render call.
float pingTimer = 0.0f
 Accumulator for periodic PING sends.
Uint64 statsPrevTime = 0
 Perf counter at the last stats snapshot.
int statsPhysTicks = 0
 Physics ticks accumulated since last snapshot.
int statsRenderFrames = 0
 Rendered frames accumulated since last snapshot.
float measuredPhysicsHz = 0.0f
 Computed physics rate (Hz).
float statsFPSCurrent = 0.0f
 Average render FPS over the last stats window (actual frames / elapsed time).
float statsFPSMin = 0.0f
 Minimum FPS in the ring buffer.
float statsFPSMax = 0.0f
 Maximum FPS in the ring buffer.
float statsFPS1pLow = 0.0f
 1st-percentile FPS (1 % low).
float statsFPS5pLow = 0.0f
 5th-percentile FPS (5 % low).
float benchSeconds_ = 0.0f
 Bench duration in seconds (0 = disabled).
Uint64 benchStartTime_ = 0
 Perf counter at first iterate() in bench mode.
bool benchActive_ = false
 True after BENCH_SECONDS read at init.
std::vector< float > benchFrameTimesMs_
 Per-frame ms after warmup; reservation in init().
std::vector< ClientPerfFramebenchFrameStats_
 Per-frame phase-time breakdown.
ClientPerfRecorder perfRecorder_
float perfSnapshotApplyMs_ = 0.0f
std::uint32_t perfSnapshotApplyCount_ = 0
MatchPhase currentMatchPhase = MatchPhase::LOBBY
 Latest match phase update from the server.
ClientId currentWinnerId = ClientId{-1}
 ClientId of the current match winner, if in POSTMATCH.
float countdownTimer = 0.0f
 Countdown timer for transitions between match phases (e.g. warmup to in-progress).
std::optional< PostMatchResultcachedPostMatchResult_
 Final scoreboard snapshot captured during FINISHED.
bool returnToLobbyRequested = false
 Latched true when server sends MATCH_STATE with phase == LOBBY.
bool returnToMainMenuRequested_ = false
 Latched true when the pause menu or disconnect requests leaving.
bool serverShutdownNoticeRequested_ = false
 Latched true when leaving because the server connection closed.
std::vector< KillFeedEventkillFeed
 Recent kill events for on-screen kill feed (newest first).
PauseMenu pauseMenu
 In-game pause menu (opened with ESC, blocks input to the game when active).
glm::vec3 cachedMuzzleWorld_ {0.0f}
bool cachedMuzzleValid_ = false
glm::vec3 cachedRightPalmWorld_ {0.0f}
bool cachedRightPalmValid_ = false
glm::vec3 muzzleFlashLightOffset_ {-12.25f, 0.0f, -10.0f}
 Tunable point-light offset from the muzzle (fwd/up/right).
std::vector< TransientVfxLighttransientVfxLights_

Static Private Attributes

static constexpr int k_physicsHz = 128
 Target physics tick rate.
static constexpr float k_physicsDt = 1.0f / static_cast<float>(k_physicsHz)
 Seconds per tick.
static constexpr int k_maxTicksPerFrame = 2
 Spiral-of-death guard: max physics ticks per iterate().
static constexpr int k_fpsHistorySize = 512
 Samples in the rolling FPS ring buffer.
static constexpr float k_benchWarmupSeconds = 2.0f
 Skip the first N seconds (pipeline warmup).

Additional Inherited Members

Static Protected Member Functions inherited from IScreen
static SDL_AppResult processCommonImguiEvent (SDL_Event *event)
 Forward event to ImGui and detect SDL_EVENT_QUIT.
static bool handleSystemMenuEvent (SDL_Event *event, SystemMenuOverlay &menu, UserSettings *settings)
 Handle Escape-toggle of a SystemMenuOverlay and forward consumed events.
static void beginMenuFrame (NewRenderer *renderer)
 Start a new ImGui frame and paint the shared menu background.
static void presentMenuFrame (NewRenderer &renderer)
 Render the ImGui draw data and present a default-camera frame.

Detailed Description

Top-level client game object.

Owns the active game screen subsystems and borrows App-owned window, renderer, and network client. Wired through App into SDL's application-callback API.

Member Enumeration Documentation

◆ HandMountDebugPoint

enum class Game::HandMountDebugPoint : std::uint8_t
strongprivate
Enumerator
Shoulder 
Elbow 
Palm 
Finger0 
Finger1 
Finger2 
Finger3 
Finger4 

◆ HandMountDebugSpace

enum class Game::HandMountDebugSpace : std::uint8_t
strongprivate
Enumerator
None 
ThirdPerson 
FirstPerson 

Member Function Documentation

◆ adoptGamepad()

void Game::adoptGamepad ( SDL_JoystickID id)
private

Open the gamepad with the given SDL_JoystickID and make it the active controller, but only if none is currently bound (first-device-wins policy).

Shared by the SDL_EVENT_GAMEPAD_ADDED hot-plug path and the init-time scan below, so both routes apply identical state + logging.

Here is the caller graph for this function:

◆ appendChatMessage()

void Game::appendChatMessage ( ClientId sender,
std::string_view message )
private

Queue a replicated chat message for HUD display.

Here is the caller graph for this function:

◆ appendLocalChatMessage()

void Game::appendLocalChatMessage ( std::string_view message)
private

Queue a locally-authored chat echo while waiting for server replication.

Here is the caller graph for this function:

◆ appendPopupMessage()

void Game::appendPopupMessage ( HudPopupKind kind,
std::string_view message )
private

Queue a generic transient HUD popup message.

Here is the caller graph for this function:

◆ applyFrameRateLimit()

void Game::applyFrameRateLimit ( )
private

Apply FPS-limit strategy based on limitFPSToMonitor, monitor Hz, and physics Hz.

Advance one frame: decoupled physics / render loop.

When monitor refresh >= physics Hz, uses VSync. Otherwise falls back to a software frame limiter at physics Hz with mailbox/immediate presentation.

Physics ALWAYS runs at exactly 128 Hz (k_physicsHz) using an accumulator with a multi-tick catch-up loop (up to k_maxTicksPerFrame per call). This is non-negotiable: it must match the server tick rate.

Input is split into two independent streams:

Mouse look (yaw / pitch) – sampled EVERY iterate() call so camera rotation is perfectly smooth at whatever frame rate the renderer produces. The camera always uses the latest yaw directly (never interpolated). Interpolating yaw with the physics alpha creates a timebase mismatch on multi-tick or zero-tick frames, producing visible jitter.

Movement keys (WASD / jump / crouch) – sampled once per physics tick group when inputSyncedWithPhysics is true (the default) so movement calculations match the server. When the toggle is off, keys are also sampled every iterate() call.

Position interpolation uses alpha = accumulator / k_physicsDt across the LAST physics tick (PreviousPosition is saved inside the while loop before each tick).

Three ImGui-tunable flags:

renderSeparateFromPhysics – render every iterate() call with position interpolated between the last two physics ticks (true, default) vs. render only after a physics tick (false, caps render fps at 128 Hz).

inputSyncedWithPhysics – sample movement keys once per tick group (true, default, server-consistent) vs. every iterate() call (false). Mouse look is always per-frame regardless of this toggle.

limitFPSToMonitor – when ON and monitor >= physicsHz, uses VSync. When monitor < physicsHz (regardless of this toggle), a software frame limiter at physicsHz is always active to ensure rock-steady frame pacing — the monitor can't display above its refresh rate anyway, and uncapped rendering creates beat-frequency jitter.

Here is the caller graph for this function:

◆ applyIncomingSnapshot()

bool Game::applyIncomingSnapshot ( std::uint32_t snapshotTick,
const std::uint8_t * bytes,
Uint32 size,
Uint64 captureNs,
std::uint32_t & ackedTick )
private

Deserialize a snapshot from the server and update the ECS registry.

Returns
True on success; false if the snapshot could not be applied.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ attachAnimatedCharacter()

void Game::attachAnimatedCharacter ( entt::entity e)
private

Attach a fresh AnimatedCharacter component to an entity.

Creates a new CharacterAnimator wired to the shared rig + clip library + skinning backend, uploads a per-entity clone of the rig's template model, and emplaces the component. Safe to call even if the rig failed to load (logs a warning and leaves the entity un-animated).

Here is the caller graph for this function:

◆ captureLocalPredictedState()

std::optional< Game::PredictedPlayerState > Game::captureLocalPredictedState ( ) const
nodiscardprivate
Here is the caller graph for this function:

◆ clearGameplayInputForChat()

void Game::clearGameplayInputForChat ( )
private

Clear held gameplay actions so typing chat cannot leak into movement or weapons.

Here is the caller graph for this function:

◆ clearPredictedStateHistory()

void Game::clearPredictedStateHistory ( )
privatenoexcept
Here is the caller graph for this function:

◆ closeChat()

void Game::closeChat ( )
private

Leave chat input mode and restore normal gameplay input capture.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ consumePostMatchResult()

std::optional< PostMatchResult > Game::consumePostMatchResult ( )

Return the final scoreboard snapshot captured before the server reset match stats.

◆ consumeReturnToMainMenu()

bool Game::consumeReturnToMainMenu ( )

True if the user requested leaving the match for the main menu, then clear that request.

◆ consumeServerShutdownNotice()

bool Game::consumeServerShutdownNotice ( )

True if the main-menu return was caused by the server connection closing, then clear that reason.

◆ evaluateReconciliationSkip()

Game::ReconciliationDecision Game::evaluateReconciliationSkip ( const PredictedPlayerState & authoritative,
const PredictedPlayerState * predictedAtAck,
const std::optional< PredictedPlayerState > & currentBeforeSnapshot ) const
nodiscardprivatenoexcept
Here is the caller graph for this function:

◆ event()

SDL_AppResult Game::event ( SDL_Event * event)
overridevirtual

Forward an SDL event to ImGui and handle application-level keys.

Parameters
eventThe SDL event to process.
Returns
SDL_APP_SUCCESS to quit, SDL_APP_CONTINUE to keep running.

Implements IScreen.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleLocalPlayerReady()

void Game::handleLocalPlayerReady ( entt::entity local)
private

Emplace player-control components onto the mapped local entity and record it.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init()

bool Game::init ( AppContext & ctx)

Initialise all subsystems and spawn the local player entity.

Returns
False on any fatal initialisation error.
Here is the call graph for this function:

◆ initDebugUI()

bool Game::initDebugUI ( const AppContext & ctx)

Create the Game-owned ImGui context before App initialises the renderer backend.

◆ iterate()

SDL_AppResult Game::iterate ( )
overridevirtual

Advance one frame: sample input, step physics, render.

Execution flow each frame:

  1. Time accumulation — compute frame delta, detect suspend/background gaps, clamp to avoid spiral-of-death.
  2. Performance stats — refresh FPS percentiles and physics Hz every 0.5 s.
  3. Input sampling — mouse look runs every frame for smooth camera; movement keys run once per physics tick group when inputSyncedWithPhysics is true; weapon keys sampled every frame.
  4. Network — send input to server, send periodic pings, poll bandwidth.
  5. Physics — drain accumulator at 128 Hz (up to k_maxTicksPerFrame), snapshot PreviousPosition, poll network for state updates, refresh remote renderables.
  6. Camera resolve — interpolate local player position between ticks (or use post-tick position in sequential mode).
  7. Local weapon VFX — fire cooldown, spawn tracers/impacts, visual recoil kick (viewmodel-only, does not affect aim).
  8. Subsystem updates — flush dispatcher events, update particles, SFX, skeletal animation (CPU skinning + hitbox capsules).
  9. Entity render list — build world-space transforms for entities, third-person weapons, glow spheres, beam visuals, point lights.

Viewmodel — weapon sway, bob, recoil decay, camera-space transform.

  1. Frame recording — if R-key recording is active, capture frame state.
  2. FPS ring buffer — record inter-render delta for stats.
  3. Debug UI — ImGui panels (debug menu, network, particles, hitbox, lighting, viewmodel tweaker, 3P weapon tweaker, scoreboard).
  4. HUD — gather game state, update and render the HUD overlay.
  5. Render — drawFrame with interpolated camera, apply VSync changes.
  6. Software frame limiter — sleep + spin-wait if targeting above monitor refresh rate.
Returns
SDL_APP_CONTINUE normally; SDL_APP_SUCCESS on quit request.
See also
ServerGame::tick for the authoritative server-side equivalent.

< rgb=color, a=blend factor (0=no tint).

< call animator->update()

< write to instance/palette slots

< index into skinnedInstances + base into bonePalette

< Apply holdPose to the animator this frame.

< Per-weapon spine-relative offset/rotation + FK arm pose.

< Weapon-swap fade weight for the arm FK (0→1).

Implements IScreen.

◆ muzzleFlashLightPosition()

glm::vec3 Game::muzzleFlashLightPosition ( const glm::vec3 & muzzleOrigin) const
nodiscardprivate

Offset a muzzle point to the actual transient point-light position.

muzzleFlashLightOffset_ uses camera/viewmodel basis units: x = forward, y = up, z = right.

Here is the caller graph for this function:

◆ muzzleFlashOrigin()

glm::vec3 Game::muzzleFlashOrigin ( const glm::vec3 & fallback) const
nodiscardprivate

World position to spawn local weapon particles.

Prefer the weapon model's tagged muzzle marker (is_muzzle / socket_muzzle) when available; fall back to the old palm-derived point for untagged guns.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ onWeaponFired()

void Game::onWeaponFired ( const struct WeaponFiredEvent & evt)
private

Dispatcher subscriber: when ANY player fires a shot, push a per-weapon-class additive pitch impulse onto that player's spine.

The kick decays inside CharacterAnimator::runSamplingAndSkinning so the upper body rises and settles back to the sampled aim pose over ~250 ms. Wired to the same WeaponFiredEvent channel SfxSystem listens on, so the same event drives both audio and visuals.

◆ openChat()

void Game::openChat ( )
private

Enter chat input mode and release normal gameplay input capture.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ predictedStateForTick()

const Game::PredictedPlayerState * Game::predictedStateForTick ( std::uint32_t tick) const
nodiscardprivatenoexcept
Here is the caller graph for this function:

◆ quit()

void Game::quit ( )
overridevirtual

Shut down all subsystems in reverse-init order.

Implements IScreen.

Here is the call graph for this function:

◆ refreshDroppedWeaponRenderables()

void Game::refreshDroppedWeaponRenderables ( )

Assign Renderable components to dropped-weapon entities (mirrors spawner visuals).

Here is the caller graph for this function:

◆ refreshRemoteHealthPackRenderables()

void Game::refreshRemoteHealthPackRenderables ( )

Assign or update Renderable components for replicated health pack spawner entities.

Here is the caller graph for this function:

◆ refreshRemotePlayerRenderables()

void Game::refreshRemotePlayerRenderables ( )

Update Renderable components for remote players (model, scale, orientation from animation rig).

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refreshRemotePowerupRenderables()

void Game::refreshRemotePowerupRenderables ( )

Assign or update Renderable components for replicated powerup entities.

Here is the caller graph for this function:

◆ refreshRemoteProjectileRenderables()

void Game::refreshRemoteProjectileRenderables ( )

Assign Renderable components to newly spawned projectile entities.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refreshRemoteRespawnRenderables()

void Game::refreshRemoteRespawnRenderables ( )

Reset Renderable visibility for players transitioning through respawn.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ restoreLocalPredictedState()

void Game::restoreLocalPredictedState ( const PredictedPlayerState & state)
private
Here is the caller graph for this function:

◆ scanForConnectedGamepads()

void Game::scanForConnectedGamepads ( )
private

Open the first already-connected gamepad, if any.

SDL fires SDL_EVENT_GAMEPAD_ADDED for pads present at SDL_Init time, but those events are delivered to whatever screen is active then (the lobby / Home), not the Game which doesn't exist yet — so a controller plugged in before the match starts would otherwise never be bound until the user physically reconnected it. Game::init() calls this to enumerate SDL_GetGamepads() and adopt the first one, covering the "connected before launch" case; the event handler still covers runtime hot-plug.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ shouldReturnToLobby()

bool Game::shouldReturnToLobby ( ) const

True once the server has returned the match phase to the lobby.

◆ shutdownAfterRenderer()

void Game::shutdownAfterRenderer ( )
overridevirtual

Destroy the Game-owned ImGui context after App shuts down the renderer backend.

Reimplemented from IScreen.

◆ spawnExplosionFlashLight()

void Game::spawnExplosionFlashLight ( const glm::vec3 & pos,
WeaponType weaponType,
float radius )
private

Spawn a transient explosion point light at pos.

Here is the caller graph for this function:

◆ spawnMuzzleFlashLight()

void Game::spawnMuzzleFlashLight ( const glm::vec3 & pos,
WeaponType weaponType )
private

Spawn a transient muzzle-flash point light at pos.

Here is the caller graph for this function:

◆ storePredictedPlayerState()

void Game::storePredictedPlayerState ( std::uint32_t tick)
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ submitChat()

void Game::submitChat ( )
private

Send the current chat draft to the server and close the chat box.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ updateCachedPostMatchResult()

void Game::updateCachedPostMatchResult ( )
private

Refresh the final post-match scoreboard snapshot while FINISHED stats are still replicated.

Here is the call graph for this function:

Member Data Documentation

◆ accumLastHitType_

uint8_t Game::accumLastHitType_ = 0
private

0=health(white), 1=shield(blue), 2=headshot(gold).

◆ accumResetTimer_

float Game::accumResetTimer_ = 0.f
private

Timer to reset accumulator after inactivity.

◆ accumTarget_

entt::entity Game::accumTarget_ = entt::null
private

Current target being damaged.

◆ accumTotal_

int Game::accumTotal_ = 0
private

Running damage total.

◆ accumulator

float Game::accumulator = 0.0f
private

Unprocessed physics time in seconds.

◆ activeGamepad_

SDL_Gamepad* Game::activeGamepad_ = nullptr
private

Currently-bound gamepad, or nullptr if none is plugged in.

Opened by scanForConnectedGamepads() at init (for pads already plugged in before the match) and on SDL_EVENT_GAMEPAD_ADDED for runtime hot-plug (first device wins — extra controllers are ignored until the active one disconnects), closed on SDL_EVENT_GAMEPAD_REMOVED. SDL3's gamepad mapping database normalises every supported controller (Xbox 360 / One, DualShock, Switch Pro, ...) onto the same logical buttons + axes, so the input mapping in InputSampleSystem.hpp works uniformly across devices.

◆ activeGamepadId_

SDL_JoystickID Game::activeGamepadId_ = 0
private

SDL_JoystickID of the active gamepad — needed to identify the device on SDL_EVENT_GAMEPAD_REMOVED so we don't tear down a different controller when a second one disconnects.

◆ aimAssistCfg_

systems::GamepadAimAssistConfig Game::aimAssistCfg_
private

AAA-style gamepad aim assist tuning.

Active only when a gamepad is connected (mouse input is unaffected). Defaults are tuned for assist not auto-aim: a stationary enemy gets zero rotational pull, a moving enemy gets partial tracking help. Live-tunable from the ECS inspector.

◆ aimAssistParityAccumSec_

float Game::aimAssistParityAccumSec_ = 0.0f
private

Seconds since the last aim-assist parity check log line (Phase F).

◆ aimAssistState_

systems::GamepadAimAssistState Game::aimAssistState_
private

Persistent inter-frame state for aim assist (anchor on target AABB + previous-frame snapshot used to compute the angular delta from which the rotational pull is derived).

Reset implicitly when the target is lost or aim assist is disabled.

◆ animLibrary_

AnimationLibrary Game::animLibrary_
private

Collection of ozz clips on the shared rig.

◆ animUI_

AnimationTesterState Game::animUI_
private

Persistent state for the Animation Tester panel.

◆ assets_

AssetRegistry Game::assets_
private

◆ authoredFPHandMountParams_

FirstPersonHandMountParams Game::authoredFPHandMountParams_[kRenderableWeaponTypeCount]
private

Defaults loaded from authored weapon assets.

◆ authoredWeaponHoldPoses_

std::array<WeaponHoldPose, kRenderableWeaponTypeCount> Game::authoredWeaponHoldPoses_ {}
private

Compile-time defaults.

◆ beamAudioStates_

std::unordered_map<entt::entity, BeamAudioState> Game::beamAudioStates_
private

◆ beamColor_

glm::vec3 Game::beamColor_ {0.6f, 0.1f, 1.0f}
private

Beam emissive colour (purple).

◆ beamEnabled_

bool Game::beamEnabled_ = false
private

Show the bloom beam.

◆ beamEndOff_

glm::vec3 Game::beamEndOff_ {200.0f, -5.0f, 10.0f}
private

Beam end offset (fwd, up, right) from eye.

◆ beamLightIntensity_

float Game::beamLightIntensity_ = 4.0f
private

Point light intensity per sample.

◆ beamLightRange_

float Game::beamLightRange_ = 300.0f
private

Point light range per sample.

◆ beamLightSpacing_

float Game::beamLightSpacing_ = 60.0f
private

Distance between point lights along beam.

◆ beamRadius_

float Game::beamRadius_ = 3.0f
private

Beam cylinder radius.

◆ beamStartOff_

glm::vec3 Game::beamStartOff_ {30.0f, -5.0f, 10.0f}
private

Beam start offset (fwd, up, right) from eye.

◆ benchActive_

bool Game::benchActive_ = false
private

True after BENCH_SECONDS read at init.

◆ benchFrameStats_

std::vector<ClientPerfFrame> Game::benchFrameStats_
private

Per-frame phase-time breakdown.

Captured every frame in bench mode and by GROUP2_CLIENT_PERF=1 play-session recordings.

◆ benchFrameTimesMs_

std::vector<float> Game::benchFrameTimesMs_
private

Per-frame ms after warmup; reservation in init().

◆ benchSeconds_

float Game::benchSeconds_ = 0.0f
private

Bench duration in seconds (0 = disabled).

◆ benchStartTime_

Uint64 Game::benchStartTime_ = 0
private

Perf counter at first iterate() in bench mode.

◆ cachedCamFwd_

glm::vec3 Game::cachedCamFwd_ {0.f, 0.f, 1.f}
private

◆ cachedEye_

glm::vec3 Game::cachedEye_ {0.f, 100.f, 0.f}
private

◆ cachedGravFlipped_

bool Game::cachedGravFlipped_ {false}
private

Local player gravity-flip state, updated each iterate().

◆ cachedMuzzleValid_

bool Game::cachedMuzzleValid_ = false
private

◆ cachedMuzzleWorld_

glm::vec3 Game::cachedMuzzleWorld_ {0.0f}
private

◆ cachedPostMatchResult_

std::optional<PostMatchResult> Game::cachedPostMatchResult_
private

Final scoreboard snapshot captured during FINISHED.

◆ cachedRightPalmValid_

bool Game::cachedRightPalmValid_ = false
private

◆ cachedRightPalmWorld_

glm::vec3 Game::cachedRightPalmWorld_ {0.0f}
private

◆ cameraRecoilIdleThreshold_

float Game::cameraRecoilIdleThreshold_ = 0.18f
private

Seconds off-trigger before target starts decaying.

◆ cameraRecoilOmega_

float Game::cameraRecoilOmega_ = 35.0f
private

Spring angular frequency (rad/s). Higher = snappier kick.

◆ cameraRecoilPitch_

float Game::cameraRecoilPitch_ = 0.0f
private

Spring current offset (radians; negative = looking up).

◆ cameraRecoilPitchVel_

float Game::cameraRecoilPitchVel_ = 0.0f
private

Spring velocity (rad/s).

◆ cameraRecoilTargetDecay_

float Game::cameraRecoilTargetDecay_ = 5.0f
private

Idle-recovery decay rate (1/s). Higher = faster snap-back.

◆ cameraRecoilTargetPitch_

float Game::cameraRecoilTargetPitch_ = 0.0f
private

Spring target; accumulates per shot, decays only when idle.

◆ cameraRecoilTargetYaw_

float Game::cameraRecoilTargetYaw_ = 0.0f
private

◆ cameraRecoilYaw_

float Game::cameraRecoilYaw_ = 0.0f
private

◆ cameraRecoilYawVel_

float Game::cameraRecoilYawVel_ = 0.0f
private

◆ charRig_

CharacterRig Game::charRig_
private

Shared skinned rig (skeleton + bind pose + weights).

◆ chatDraft_

std::string Game::chatDraft_
private

◆ chatMessages_

std::vector<HudChatMessage> Game::chatMessages_
private

◆ chatOpen_

bool Game::chatOpen_ = false
private

◆ client

Client* Game::client = nullptr
private

Borrowed UDP network client owned by App.

◆ clientHitboxRig_

HitboxRig Game::clientHitboxRig_
private

Hitbox definitions for client-side debug visualization.

◆ clientPredictTick

uint32_t Game::clientPredictTick = 0
private

Monotonic per-tick counter stamped onto outgoing InputSnapshots.

Bumped once per physics tick group inside iterate() and copied into the local player's InputSnapshot.tick before each send. The server uses this to dedup re-sent inputs (multi-input redundancy) and apply only inputs newer than its lastAppliedInputTick. Phase-5 prediction also keys the input ring buffer by it.

◆ committedRecoilPitch_

float Game::committedRecoilPitch_ = 0.0f
private

Portion of cameraRecoilPitch_ already added to snap.pitch.

◆ committedRecoilYaw_

float Game::committedRecoilYaw_ = 0.0f
private

Each frame we commit (current - committed) and update.

◆ compensationCreditCap_

float Game::compensationCreditCap_ = 0.10f
private

Max banked credit (rad). ~5.7° per axis.

◆ compensationCreditDecay_

float Game::compensationCreditDecay_ = 1.0f
private

Credit decay rate (1/s); stale credit fades.

◆ compensationCreditPitch_

float Game::compensationCreditPitch_ = 0.0f
private

◆ compensationCreditYaw_

float Game::compensationCreditYaw_ = 0.0f
private

◆ countdownTimer

float Game::countdownTimer = 0.0f
private

Countdown timer for transitions between match phases (e.g. warmup to in-progress).

◆ currentCameraRoll_

float Game::currentCameraRoll_ {0.0f}
private

Smoothed camera roll angle (radians).

◆ currentEquippedType_

WeaponType Game::currentEquippedType_ = WeaponType::Rifle
private

Cached each frame.

◆ currentMatchPhase

MatchPhase Game::currentMatchPhase = MatchPhase::LOBBY
private

Latest match phase update from the server.

◆ currentWinnerId

ClientId Game::currentWinnerId = ClientId{-1}
private

ClientId of the current match winner, if in POSTMATCH.

◆ damagePowerupModelIdx_

int Game::damagePowerupModelIdx_ = -1
private

◆ debugUI

DebugUI Game::debugUI
private

Owns the ImGui context and SDL3 input backend.

◆ dispatcher

entt::dispatcher Game::dispatcher
private

Event bus for weapon/impact/explosion events.

◆ dissolveSpawned_

std::unordered_set<entt::entity> Game::dissolveSpawned_
private

Entities for which a death-dissolve particle burst has already been spawned (so we emit it exactly once per death).

Cleared per-entity on respawn. See the death-capture pass in the render submit.

◆ emoteCamBlend_

float Game::emoteCamBlend_ {0.0f}
private

0 = first-person, 1 = third-person emote cam (eased).

◆ flashlightEnabled_

bool Game::flashlightEnabled_ = false
private

Point light at camera position.

◆ flashlightIntensity_

float Game::flashlightIntensity_ = 8.0f
private

Flashlight brightness.

◆ flashlightOffset_

float Game::flashlightOffset_ = 30.0f
private

Forward offset from eye.

◆ flashlightRange_

float Game::flashlightRange_ = 800.0f
private

Flashlight attenuation range.

◆ footstepCooldowns_

std::unordered_map<entt::entity, float> Game::footstepCooldowns_
private

◆ footstepPhases_

std::unordered_map<entt::entity, std::array<float, 5> > Game::footstepPhases_
private

◆ fpHandMountParams_

FirstPersonHandMountParams Game::fpHandMountParams_[kRenderableWeaponTypeCount]
private

Runtime-tunable copy; initialised from defaults.

◆ fpHandMountTuneWeaponIdx_

int Game::fpHandMountTuneWeaponIdx_ = 0
private

Which weapon type is being tuned.

◆ fpsHistory

float Game::fpsHistory[k_fpsHistorySize] = {}
private

Circular buffer of per-frame FPS samples.

◆ fpsHistoryCount

int Game::fpsHistoryCount = 0
private

Valid sample count (saturates at k_fpsHistorySize).

◆ fpsHistoryHead

int Game::fpsHistoryHead = 0
private

Next write index.

◆ frameCount

uint64_t Game::frameCount = 0
private

Monotonic render-frame counter.

◆ gamepadLookSensitivity

float Game::gamepadLookSensitivity = 6.0f
private

Right-stick look speed in radians per second at full deflection.

6.0 rad/s ≈ 343°/s — most testers found 3.0 too sluggish for tracking players during firefights; this is in line with mainstream console FPS defaults. Tunable from the ECS inspector.

◆ grenadeModelIdx_

int Game::grenadeModelIdx_ = -1
private

◆ grenadeThrowDownwardOffset_

float Game::grenadeThrowDownwardOffset_ = 0.0f
private

Downward offset for the grenade-throw animation.

◆ gripSwapState_

std::unordered_map<entt::entity, GripSwapState> Game::gripSwapState_ {}
private

◆ handMountDebugMarkerModelIdx_

int Game::handMountDebugMarkerModelIdx_ = -1
private

◆ handMountDebugTarget_

HandMountDebugTarget Game::handMountDebugTarget_ {}
private

◆ haveLastSnap_

bool Game::haveLastSnap_ = false
private

Becomes true after the first frame the local player exists.

◆ heGrenadeModelIdx_

int Game::heGrenadeModelIdx_ = -1
private

◆ hitmarkerIsHeadshot_

bool Game::hitmarkerIsHeadshot_ = false
private

True when the current hitmarker was a headshot.

◆ hitmarkerShieldBreak_

bool Game::hitmarkerShieldBreak_ = false
private

True when the current hit depleted target armor.

◆ hitmarkerTimer_

float Game::hitmarkerTimer_ = 0.0f
private

Remaining display time (fades out over this).

◆ holdPoseReloadAccumulator_

float Game::holdPoseReloadAccumulator_ = 0.0f
private

Seconds since last hot-reload mtime poll (~4 Hz throttle).

◆ holdRotStepDeg_

float Game::holdRotStepDeg_ = 5.0f
private

Delta-rotation step (deg) for the spine-rotation buttons.

◆ holdShowDebugMarker_

bool Game::holdShowDebugMarker_ = true
private

Render the spine-anchor axis marker when the tweaker is open.

◆ horizontalFovDegrees

float Game::horizontalFovDegrees = 90.0f
private

Player-facing horizontal camera field of view in degrees.

◆ hud_

Hud Game::hud_
private

In-game HUD overlay system.

◆ inputRing_

InputRingBuffer Game::inputRing_
private

Phase 5b: ring buffer of recent stamped inputs for replay- based reconciliation.

Each entry is keyed by clientPredictTick so runReconciliation can look up the input that was sent for any recent tick and feed it back into runMovement during replay.

◆ inputSyncedWithPhysics

bool Game::inputSyncedWithPhysics = true
private

Sample mouse once per physics tick (true) vs every iterate() call (false).

◆ k_benchWarmupSeconds

float Game::k_benchWarmupSeconds = 2.0f
staticconstexprprivate

Skip the first N seconds (pipeline warmup).

◆ k_fpsHistorySize

int Game::k_fpsHistorySize = 512
staticconstexprprivate

Samples in the rolling FPS ring buffer.

◆ k_maxTicksPerFrame

int Game::k_maxTicksPerFrame = 2
staticconstexprprivate

Spiral-of-death guard: max physics ticks per iterate().

Dropped from 8 to 2 in Phase 3g — the bench profiler showed the slowest frames are dominated by catch-up bursts (e.g. 8 ticks × ~2 ms = 16 ms in one frame, dragging p1/p5). Capping at 2 spreads the catch-up across more render frames so any individual frame's worst case is ~4 ms instead of ~16 ms. Visual consequence on a stall: physics simulation briefly runs 0.5× wall speed until the accumulator drains naturally; human-imperceptible at 1000+ render Hz.

◆ k_physicsDt

float Game::k_physicsDt = 1.0f / static_cast<float>(k_physicsHz)
staticconstexprprivate

Seconds per tick.

◆ k_physicsHz

int Game::k_physicsHz = 128
staticconstexprprivate

Target physics tick rate.

◆ killcamActive_

bool Game::killcamActive_ {false}
private

True this frame while the killcam is driving the camera.

◆ killcamEye_

glm::vec3 Game::killcamEye_ {0.0f}
private

Eye position locked at the moment of death.

◆ killcamKillerCenter_

glm::vec3 Game::killcamKillerCenter_ {0.0f}
private

Killer AABB center (world) for the HUD label.

◆ killcamKillerEntity_

entt::entity Game::killcamKillerEntity_ {entt::null}
private

Killer entity (drives the chams pass), or null.

◆ killcamKillerHalf_

glm::vec3 Game::killcamKillerHalf_ {0.0f}
private

Killer AABB half-extents (world) for the HUD label.

◆ killcamKillerName_

std::string Game::killcamKillerName_
private

Killer's display nickname (for the HUD label).

◆ killcamPitch_

float Game::killcamPitch_ {0.0f}
private

Smoothed killcam pitch (radians).

◆ killcamYaw_

float Game::killcamYaw_ {0.0f}
private

Smoothed killcam yaw (radians).

◆ killFeed

std::vector<KillFeedEvent> Game::killFeed
private

Recent kill events for on-screen kill feed (newest first).

◆ kRigScale_

float Game::kRigScale_ = 1.0f
private

Per-renderable scale for animated characters (auto-calculated, tunable).

◆ kRigVerticalOffset_

float Game::kRigVerticalOffset_
private
Initial value:
=
-90.0f

Per-renderable Y translation for animated characters (auto-calculated, tunable).

◆ lastEquippedType_

WeaponType Game::lastEquippedType_ = WeaponType::Rifle
private

Previous frame's weapon — triggers default reload on change.

◆ lastInputDevice_

BindingDevice Game::lastInputDevice_ {}
private

Last input device the player actually used, for HUD glyph selection.

Flips to Controller on gamepad button/stick/trigger input and back to KeyboardMouse on key/mouse input. Value-init is KeyboardMouse (0).

◆ lastShotgunBlast_

HudShotgunBlast Game::lastShotgunBlast_ {}
private

Most recently completed blast (staged for HUD).

◆ lastSnapPitchAfterCommit_

float Game::lastSnapPitchAfterCommit_ = 0.0f
private

snap.pitch saved at end of last spring tick — diff against

◆ lastSnapYawAfterCommit_

float Game::lastSnapYawAfterCommit_ = 0.0f
private

current to recover the player's mouse delta this frame.

◆ limitFPSToMonitor

bool Game::limitFPSToMonitor = true
private

VSync on (true) / off (false).

Default ON: caps to the monitor refresh, which frees GPU headroom and removes the near-light stutter. Toggle live via the debug menu; the GROUP2_CLIENT_UNCAPPED env var / bench mode still force it off.

◆ localEmote_

int Game::localEmote_ {-1}
private

Active local emote index (EmoteCatalog), or -1.

◆ localFireCooldown_

float Game::localFireCooldown_ = 0.0f
private

Countdown timer; fire VFX only when <= 0.

◆ localRecoilHeat_

float Game::localRecoilHeat_ = 0.0f
private

◆ localWasDead_

bool Game::localWasDead_ = false
private

Local player's dead state last frame — used to detect the dead→alive (respawn) edge and snap the view to the spawn yaw.

◆ mapCollision_

physics::MapCollisionData Game::mapCollision_
private

◆ mappedLocalPlayerEntity_

std::optional<entt::entity> Game::mappedLocalPlayerEntity_
private

Local-registry entity for this client's player, once assigned.

◆ matchElapsedSeconds_

float Game::matchElapsedSeconds_ = 0.f
private

Time accumulated while the match is in PLAYING phase (s).

Drives the top-center match-header readout. Reset on phase changes other than PLAYING so a fresh match starts at 0:00.

◆ measuredPhysicsHz

float Game::measuredPhysicsHz = 0.0f
private

Computed physics rate (Hz).

◆ medkitModelIdx_

int Game::medkitModelIdx_ = -1
private

◆ molotovModelIdx_

int Game::molotovModelIdx_ = -1
private

◆ mouseCaptured

bool Game::mouseCaptured = true
private

True when relative mouse mode is active.

◆ mouseSensitivity

float Game::mouseSensitivity = user_settings::kDefaultMouseSensitivity
private

Radians per pixel of mouse movement.

◆ movableSphereEnabled_

bool Game::movableSphereEnabled_ = false
private

Glow sphere following the player.

◆ muzzleFlashLightOffset_

glm::vec3 Game::muzzleFlashLightOffset_ {-12.25f, 0.0f, -10.0f}
private

Tunable point-light offset from the muzzle (fwd/up/right).

◆ particleSystem

ParticleSystem Game::particleSystem
private

Client-side VFX particle system.

◆ pauseMenu

PauseMenu Game::pauseMenu
private

In-game pause menu (opened with ESC, blocks input to the game when active).

◆ pendingDamageNumbers_

std::vector<PendingDamageNumber> Game::pendingDamageNumbers_
private

◆ pendingLocalChatEchoes_

std::deque<std::string> Game::pendingLocalChatEchoes_
private

◆ pendingPickupNotifications_

std::vector<HudPickupNotification> Game::pendingPickupNotifications_
private

Pickup notifications queued for the next HUD frame.

Emitted when the local player's WeaponState gains a new weapon type or their reserve ammo grows beyond the previous frame's reading.

◆ pendingPopupMessages_

std::vector<HudPopupMessage> Game::pendingPopupMessages_
private

Generic popup notifications queued for the next HUD frame.

The HUD widget consumes each entry once, then owns display lifetime, animation, and expiration.

◆ pendingScrollSwitch_

int Game::pendingScrollSwitch_ = 0
private

+1 = next slot, -1 = previous slot, consumed each frame.

◆ perfRecorder_

ClientPerfRecorder Game::perfRecorder_
private

◆ perfSnapshotApplyCount_

std::uint32_t Game::perfSnapshotApplyCount_ = 0
private

◆ perfSnapshotApplyMs_

float Game::perfSnapshotApplyMs_ = 0.0f
private

◆ pingTimer

float Game::pingTimer = 0.0f
private

Accumulator for periodic PING sends.

◆ playerSfxState_

std::unordered_map<entt::entity, PlayerSfxState> Game::playerSfxState_
private

◆ predictedStateHistory_

std::array<PredictedPlayerState, InputRingBuffer::k_capacity> Game::predictedStateHistory_ {}
private

◆ prevAmmoReserve_

int Game::prevAmmoReserve_ = -1
private

Drives "+N <weapon> AMMO" reserve-grow notifications.

◆ prevArmor_

float Game::prevArmor_ = 100.f
private

◆ prevHealth_

float Game::prevHealth_ = 100.f
private

◆ prevPrimaryWeaponType_

int Game::prevPrimaryWeaponType_ = -1
private

Last-frame snapshot of the local player's weapon-slot types (-1 = empty).

Used to detect new weapons appearing → emit a pickup notification ("+1 RIFLE") so the side feed reflects what just changed.

◆ prevRenderTime

Uint64 Game::prevRenderTime = 0
private

Perf counter at the last render call.

◆ prevSecondaryWeaponType_

int Game::prevSecondaryWeaponType_ = -1
private

◆ prevShootingForDebug_

bool Game::prevShootingForDebug_ = false
private

PR-20: tracks last frame's input.shooting so the fire-rising-edge detector inside iterate() only captures the FIRST tick of a click (a "trigger pull"), not every tick the button is held.

Survives across frames as a member; is naturally reset when the local player respawns because the View<LocalPlayer> branch returns early during the dead-window (no InputSnapshot present). Implementation detail of the shot-debug visualizer.

◆ prevSwayPitch_

float Game::prevSwayPitch_ = 0.0f
private

◆ prevSwayYaw_

float Game::prevSwayYaw_ = 0.0f
private

◆ prevTime

Uint64 Game::prevTime = 0
private

SDL performance counter at the last iterate() call.

◆ recoilIdleTime_

float Game::recoilIdleTime_ = 0.0f
private

◆ recoilPatternScaleMultiplier_

float Game::recoilPatternScaleMultiplier_ = 2.0f
private

Live multiplier on WeaponConfig.recoilPatternScale.

1.0 = nominal; 2.0 = double recoil. Tune in debug UI.

◆ recoilPitch_

float Game::recoilPitch_ = 0.0f
private

Current recoil pitch offset (degrees).

◆ recoilPushBack_

float Game::recoilPushBack_ = 0.0f
private

Current recoil backward offset (Quake units).

◆ recoilRoll_

float Game::recoilRoll_ = 0.0f
private

Current recoil roll offset (degrees).

◆ recorder

FrameRecorder Game::recorder
private

R-key toggled frame-state + screenshot recorder.

◆ registry

Registry Game::registry
private

The shared ECS registry.

◆ reloadDownwardOffset_

float Game::reloadDownwardOffset_ = 0.0f
private

Downward offset for the reload animation.

◆ renderer

NewRenderer* Game::renderer = nullptr
private

Borrowed renderer owned by App.

◆ renderSeparateFromPhysics

bool Game::renderSeparateFromPhysics = true
private

Render every iterate() with interpolation (true) vs only after a physics tick (false).

◆ returnToLobbyRequested

bool Game::returnToLobbyRequested = false
private

Latched true when server sends MATCH_STATE with phase == LOBBY.

◆ returnToMainMenuRequested_

bool Game::returnToMainMenuRequested_ = false
private

Latched true when the pause menu or disconnect requests leaving.

◆ rightHandJointIdx_

int Game::rightHandJointIdx_ = -1
private

Cached "mixamorig:RightHand" joint index (-1 = not present).

◆ rigMeshMinY_

float Game::rigMeshMinY_ = 0.0f
private

Minimum Y of the bind-pose mesh vertices (model space).

◆ rocketProjectileModelIdx_

int Game::rocketProjectileModelIdx_ = -1
private

◆ serverShutdownNoticeRequested_

bool Game::serverShutdownNoticeRequested_ = false
private

Latched true when leaving because the server connection closed.

◆ sfxSystem

SfxSystem* Game::sfxSystem = nullptr
private

Borrowed audio system owned by App.

◆ shieldPowerupModelIdx_

int Game::shieldPowerupModelIdx_ = -1
private

◆ shotgunPelletAccum_

HudShotgunBlast Game::shotgunPelletAccum_ {}
private

In-flight accumulator (resets when 9 pellets received).

◆ shotgunPelletAccumCount_

int Game::shotgunPelletAccumCount_ = 0
private

Pellets received for the current in-flight blast.

◆ shotgunPelletLastTimeSec_

float Game::shotgunPelletLastTimeSec_ = 0.f
private

Game-time of the last pellet (for stale-reset).

◆ showDynLightUI_

bool Game::showDynLightUI_ = false
private

Show the Dynamic Lighting panel.

◆ showFPHandMountUI_

bool Game::showFPHandMountUI_ = false
private

Show the FP Arm Tweaker window.

◆ showHdrDebugUI_

bool Game::showHdrDebugUI_ = false
private

Show the HDR / tonemap debug panel.

◆ showHudDebug_

bool Game::showHudDebug_ = false
private

Show the HUD Tweaker panel.

◆ showMenuThemeUI_

bool Game::showMenuThemeUI_ = false
private

Show the Menu Theme Tweaker panel.

◆ showViewmodelUI

bool Game::showViewmodelUI = false
private

Show the Viewmodel Tweaker window.

◆ showWeaponHoldUI_

bool Game::showWeaponHoldUI_ = false
private

Show the Weapon Hold tweaker window.

◆ showWeaponSpawnerModelUI_

bool Game::showWeaponSpawnerModelUI_ = false
private

Show the Weapon Spawner Model Tweaker window.

◆ skinBackend_

CpuLbsSkinningBackend Game::skinBackend_
private

Phase-1 CPU linear-blend-skinning backend.

◆ snapshotLoader_

std::optional<registry_serialization::Loader> Game::snapshotLoader_
private

Incremental loader; created on first snapshot.

◆ softLimitNextFrame

Uint64 Game::softLimitNextFrame = 0
private

Performance counter target for next frame deadline.

◆ softLimitPeriod

Uint64 Game::softLimitPeriod = 0
private

Target frame period in perf-counter ticks (0 = disabled).

◆ spawnerTuneWeaponIdx_

int Game::spawnerTuneWeaponIdx_ = 0
private

Which weapon type is being tuned.

◆ spawnerWeaponParams_

WeaponSpawnerModelParams Game::spawnerWeaponParams_[kRenderableWeaponTypeCount]
private

Runtime-tunable copy; initialised from spawner defaults.

◆ sphereFollowDist_

float Game::sphereFollowDist_ = 150.0f
private

Distance ahead of player.

◆ sphereIntensity_

float Game::sphereIntensity_ = 5.0f
private

Point light intensity of movable sphere.

◆ sphereRange_

float Game::sphereRange_ = 500.0f
private

Point light range of movable sphere.

◆ spine2JointIdx_

int Game::spine2JointIdx_ = -1
private

Cached "mixamorig:Spine2" joint index.

The weapon is a rigid child of this bone and the FK arm hold hangs off it.

◆ statsFPS1pLow

float Game::statsFPS1pLow = 0.0f
private

1st-percentile FPS (1 % low).

◆ statsFPS5pLow

float Game::statsFPS5pLow = 0.0f
private

5th-percentile FPS (5 % low).

◆ statsFPSCurrent

float Game::statsFPSCurrent = 0.0f
private

Average render FPS over the last stats window (actual frames / elapsed time).

◆ statsFPSMax

float Game::statsFPSMax = 0.0f
private

Maximum FPS in the ring buffer.

◆ statsFPSMin

float Game::statsFPSMin = 0.0f
private

Minimum FPS in the ring buffer.

◆ statsPhysTicks

int Game::statsPhysTicks = 0
private

Physics ticks accumulated since last snapshot.

◆ statsPrevTime

Uint64 Game::statsPrevTime = 0
private

Perf counter at the last stats snapshot.

◆ statsRenderFrames

int Game::statsRenderFrames = 0
private

Rendered frames accumulated since last snapshot.

◆ stickyGrenadeModelIdx_

int Game::stickyGrenadeModelIdx_ = -1
private

◆ swayAmplitudePitch_

float Game::swayAmplitudePitch_ = 2.0f
private

◆ swayAmplitudeYaw_

float Game::swayAmplitudeYaw_ = 3.0f
private

◆ swayDecayRate_

float Game::swayDecayRate_ = 8.0f
private

Exponential decay speed.

◆ swayInitialized_

bool Game::swayInitialized_ = false
private

Guard against initial delta spike.

◆ swayOffsetX_

float Game::swayOffsetX_ = 0.0f
private

Current horizontal sway (right axis, Quake units).

◆ swayOffsetY_

float Game::swayOffsetY_ = 0.0f
private

Current vertical sway (up axis).

◆ swaySmoothing_

float Game::swaySmoothing_ = 0.15f
private

Input smoothing (0..1, lower = smoother).

◆ tickCount

int Game::tickCount = 0
private

Total physics ticks elapsed since start.

◆ tpFreezeAnimations_

bool Game::tpFreezeAnimations_ = false
private

Debug: freeze every animator's playback while tuning.

◆ tpTuneWeaponIdx_

int Game::tpTuneWeaponIdx_ = 0
private

Which weapon type the Weapon Hold tweaker is editing.

◆ tpWeaponParams_

ThirdPersonWeaponParams Game::tpWeaponParams_[kRenderableWeaponTypeCount]
private

Runtime-tunable procedural/scale params.

◆ transientVfxLights_

std::vector<TransientVfxLight> Game::transientVfxLights_
private

◆ useRecoilCompensation_

bool Game::useRecoilCompensation_ = false
private

false = Approach A (no restore — aim keeps the view angles the recoil walked it to; player compensates manually); true = Approach B.

◆ userSettings

UserSettings* Game::userSettings = nullptr
private

Borrowed user settings owned by App.

◆ userSettingsPath_

std::string_view Game::userSettingsPath_
private

Borrowed save path for user settings.

◆ useSpringCameraRecoil_

bool Game::useSpringCameraRecoil_ = true
private

Toggle Apex-spring path vs legacy instant snap-pitch write.

◆ viewmodelDefaultsApplied_

bool Game::viewmodelDefaultsApplied_ = false
private

◆ viewmodelLeftHandModelIdx_

int Game::viewmodelLeftHandModelIdx_ = -1
private

◆ viewmodelRightHandModelIdx_

int Game::viewmodelRightHandModelIdx_ = -1
private

◆ vmDown

float Game::vmDown = 0.0f
private

Downward offset from eye.

◆ vmForward

float Game::vmForward = 0.0f
private

Forward offset from eye (Quake units).

◆ vmPitchOffset

float Game::vmPitchOffset = 0.0f
private

Extra pitch (degrees).

◆ vmRight

float Game::vmRight = 0.0f
private

Right offset from eye.

◆ vmRollOffset

float Game::vmRollOffset = 0.0f
private

Extra roll (degrees).

◆ vmScale

float Game::vmScale = 1.0f
private

Weapon model scale (model is in mm).

◆ vmYawOffset

float Game::vmYawOffset = 0.0f
private

Extra yaw (degrees) applied to the model before camera orient.

◆ voiceChat_

VoiceChatSystem Game::voiceChat_
private

Push-to-talk Opus proximity voice chat.

◆ voiceSpeakers_

std::vector<HudVoiceSpeaker> Game::voiceSpeakers_
private

◆ wasChargingRailgun_

bool Game::wasChargingRailgun_ = false
private

True last frame if local player was charging RailGun.

◆ weaponAssetIds_

int Game::weaponAssetIds_[kRenderableWeaponTypeCount] = {-1, -1, -1, -1, -1}
private

◆ weaponHoldPoseMTimes_

std::array<std::filesystem::file_time_type, kRenderableWeaponTypeCount> Game::weaponHoldPoseMTimes_ {}
private

◆ weaponHoldPosePaths_

std::array<std::string, kRenderableWeaponTypeCount> Game::weaponHoldPosePaths_ {}
private

Source TOML path each.

◆ weaponHoldPoses_

std::array<WeaponHoldPose, kRenderableWeaponTypeCount> Game::weaponHoldPoses_ {}
private

Runtime, live-tuned.

◆ weaponModelIndices_

int Game::weaponModelIndices_[kRenderableWeaponTypeCount] = {-1, -1, -1, -1, -1}
private

◆ window

SDL_Window* Game::window = nullptr
private

The application window.

◆ workerPool_

std::unique_ptr<WorkerPool> Game::workerPool_
private

Persistent thread pool for parallel-for over per-frame loops (currently the animation update; future: parallel frustum cull, particle update, ECS transforms).

Initialised in Game::init with a worker count derived from std::thread::hardware_concurrency() / 2 (or GROUP2_WORKERS if set), so we leave half the cores for the rest of the system. Gated behind a unique_ptr so it constructs AFTER init() reads the env override.


The documentation for this class was generated from the following files: