group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
InputSampleSystem.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
14
15#include <SDL3/SDL.h>
16
17#include <algorithm>
18#include <cmath>
19#include <glm/geometric.hpp>
20#include <glm/trigonometric.hpp>
21#include <glm/vec2.hpp>
22
26namespace systems
27{
28
30inline bool prevKillSelfKey = false;
32inline bool prevGrenadeCycleKey = false;
33inline bool prevGrenadeThrowKey = false;
35inline bool prevGamepadGrenadeCycleKey = false;
36inline bool prevGamepadGrenadeThrowKey = false;
38inline bool pendingGrenadeThrow = false;
40inline bool pendingGrenadeCycleNext = false;
41inline bool pendingGrenadeCyclePrev = false;
43inline bool prevAbilitySelectLeft = false;
45inline bool prevAbilitySelectRight = false;
47inline bool pendingAbilitySelectLeft = false;
48inline bool pendingAbilitySelectRight = false;
50inline bool prevGamepadAbilitySelectLeft = false;
54inline bool prevGamepadPickupKey = false;
55inline Uint64 gamepadPickupDownMs = 0;
56inline bool gamepadPickupHoldFired = false;
58inline bool pendingGamepadWeaponSwap = false;
61inline float gamepadLookAccel = 0.0f;
62
63// ─── Emote wheel state ───────────────────────────────────────────────────────
64//
65// The emote wheel mirrors the radial-selection feel of a grenade wheel: hold the
66// Emote binding to open the wheel, point (mouse delta on KBM, right stick on a
67// pad) at one of the `emotes::kEmoteCount` sectors to highlight it, and release
68// to play the highlighted emote. The highlighted sector and a latched
69// "play this emote" request are read by the game loop and the HUD widget.
70
72inline bool emoteWheelOpen = false;
74inline int emoteWheelSelection = -1;
76inline float emoteWheelDirX = 0.0f;
77inline float emoteWheelDirY = 0.0f;
79inline int pendingEmoteRequest = -1;
81inline bool prevEmoteKey = false;
82inline bool prevGamepadEmoteKey = false;
83
86{
87 SDL_GamepadAxis x;
88 SDL_GamepadAxis y;
89};
90
92inline JoystickAxis getLookJoystickAxes(bool swapSticks)
93{
94 return {
95 .x = swapSticks ? SDL_GAMEPAD_AXIS_LEFTX : SDL_GAMEPAD_AXIS_RIGHTX,
96 .y = swapSticks ? SDL_GAMEPAD_AXIS_LEFTY : SDL_GAMEPAD_AXIS_RIGHTY,
97 };
98}
99
101inline JoystickAxis getMoveJoystickAxes(bool swapSticks)
102{
103 return {
104 .x = swapSticks ? SDL_GAMEPAD_AXIS_RIGHTX : SDL_GAMEPAD_AXIS_LEFTX,
105 .y = swapSticks ? SDL_GAMEPAD_AXIS_RIGHTY : SDL_GAMEPAD_AXIS_LEFTY,
106 };
107}
108
110inline bool gamepadConnected(SDL_Gamepad* gamepad)
111{
112 return gamepad != nullptr && SDL_GamepadConnected(gamepad);
113}
114
117{
118 const bool shouldThrow = pendingGrenadeThrow;
119 pendingGrenadeThrow = false;
120 return shouldThrow;
121}
122
125{
126 const bool requested = pendingGrenadeCycleNext;
128 return requested;
129}
130
133{
134 const bool requested = pendingGrenadeCyclePrev;
136 return requested;
137}
138
141{
142 const bool requested = pendingGamepadWeaponSwap;
144 return requested;
145}
146
149{
150 const int emote = pendingEmoteRequest;
152 return emote;
153}
154
157{
158 const bool requested = pendingAbilitySelectLeft;
160 return requested;
161}
162
165{
166 const bool requested = pendingAbilitySelectRight;
168 return requested;
169}
170
179inline void emoteWheelApplyPointing(float dx, float dy, bool accumulate)
180{
181 if (accumulate) {
182 emoteWheelDirX += dx;
183 emoteWheelDirY += dy;
184 } else {
185 emoteWheelDirX = dx;
186 emoteWheelDirY = dy;
187 }
188
189 // Clamp magnitude so accumulated mouse motion can't grow without bound.
190 constexpr float k_maxMag = 400.0f;
192 if (mag2 > k_maxMag * k_maxMag) {
193 const float m = std::sqrt(mag2);
194 emoteWheelDirX *= k_maxMag / m;
195 emoteWheelDirY *= k_maxMag / m;
196 }
197
198 constexpr float k_deadzone = 28.0f;
199 if (mag2 < k_deadzone * k_deadzone) {
201 return;
202 }
203
204 // atan2(dx, -dy): 0 at top (pointing up), increasing clockwise.
205 float angle = std::atan2(emoteWheelDirX, -emoteWheelDirY);
206 if (angle < 0.0f)
207 angle += glm::two_pi<float>();
208 const float sectorSize = glm::two_pi<float>() / static_cast<float>(emotes::kEmoteCount);
209 emoteWheelSelection = static_cast<int>(std::lround(angle / sectorSize)) % emotes::kEmoteCount;
210}
211
217inline void runEmoteWheelKey(const InputBindings& bindings)
218{
219 const bool* const kKeys = SDL_GetKeyboardState(nullptr);
220 const SDL_MouseButtonFlags mouse = SDL_GetMouseState(nullptr, nullptr);
221 const bool keyNow = bindings.pressed(Action::Emote, kKeys, mouse);
222
223 if (keyNow && !prevEmoteKey) {
224 // Opening: clear any stale pointing/selection.
225 emoteWheelDirX = 0.0f;
226 emoteWheelDirY = 0.0f;
228 } else if (!keyNow && prevEmoteKey) {
229 // Releasing: fire the highlighted emote (if any).
230 if (emoteWheelSelection >= 0)
233 }
234
235 emoteWheelOpen = keyNow;
236 prevEmoteKey = keyNow;
237}
238
252inline void runMouseLook(Registry& registry, float mouseSensitivity, bool gravityFlipped = false)
253{
254 float mdx = 0.0f;
255 float mdy = 0.0f;
256 SDL_GetRelativeMouseState(&mdx, &mdy);
257
258 // When gravity is flipped the camera is rolled 180°, which swaps
259 // both screen-left/right and screen-up/down relative to world space.
260 // Negating both deltas keeps controls feeling natural.
261 if (gravityFlipped) {
262 mdx = -mdx;
263 mdy = -mdy;
264 }
265
266 // While the emote wheel is open, mouse motion picks a sector instead of
267 // turning the camera (standard radial-menu feel).
268 if (emoteWheelOpen) {
269 emoteWheelApplyPointing(mdx, mdy, /*accumulate=*/true);
270 return;
271 }
272
273 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
274 // Negate: SDL mdx is positive when moving right, but positive yaw
275 // rotates toward +X which maps to screen-left via glm::lookAt's
276 // cross(forward, up) convention. Negating gives the standard
277 // "mouse right = look right" behaviour.
278 snap.yaw -= mdx * mouseSensitivity;
279
280 // Wrap yaw to [-π, π] to avoid float precision drift over time.
281 snap.yaw = std::remainder(snap.yaw, glm::radians(360.0f));
282
283 // Clamp pitch to avoid gimbal-lock at the poles.
284 snap.pitch = std::clamp(snap.pitch + mdy * mouseSensitivity, -glm::radians(89.0f), glm::radians(89.0f));
285 });
286}
287
298inline void runMovementKeys(Registry& registry, const InputBindings& bindings, bool gravityFlipped = false)
299{
300 const bool* const kKeys = SDL_GetKeyboardState(nullptr);
301 const SDL_MouseButtonFlags mouse = SDL_GetMouseState(nullptr, nullptr);
302
303 // Edge-detect K key: only fire killSelf on the rising edge (key-down),
304 // not while held. Prevents respawn → immediate re-death loop.
305 const bool killKeyNow = bindings.pressed(Action::KillSelf, kKeys, mouse);
306 const bool killEdge = killKeyNow && !prevKillSelfKey;
307 prevKillSelfKey = killKeyNow;
308
309 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
310 // Movement keys
311 const bool forward = bindings.pressed(Action::Forward, kKeys, mouse);
312 const bool back = bindings.pressed(Action::Back, kKeys, mouse);
313 const bool left = bindings.pressed(Action::Left, kKeys, mouse);
314 const bool right = bindings.pressed(Action::Right, kKeys, mouse);
315
316 snap.forward = forward;
317 snap.back = back;
318 // When gravity is flipped the camera is rolled 180°, which negates
319 // the screen-space right vector. Swapping A/D compensates so
320 // pressing A still moves the player screen-left.
321 snap.left = gravityFlipped ? right : left;
322 snap.right = gravityFlipped ? left : right;
323 snap.jump = bindings.pressed(Action::Jump, kKeys, mouse);
324 snap.crouch = bindings.pressed(Action::Crouch, kKeys, mouse);
325 snap.ability1 = bindings.pressed(Action::Ability1, kKeys, mouse);
326 snap.ability2 = bindings.pressed(Action::Ability2, kKeys, mouse);
327 snap.killSelf = killEdge;
328 snap.skipRespawn = false; // Clear stale flag from previous death.
329 });
330}
331
339inline void runDeadInput(Registry& registry, const InputBindings& bindings)
340{
341 const bool* const kKeys = SDL_GetKeyboardState(nullptr);
342 const SDL_MouseButtonFlags mouse = SDL_GetMouseState(nullptr, nullptr);
343
344 registry.view<InputSnapshot, LocalPlayer, RespawnTimer>().each(
345 [&](InputSnapshot& snap, const RespawnTimer& /*unused*/) {
346 snap.skipRespawn = bindings.pressed(Action::Jump, kKeys, mouse);
347 });
348}
349
358inline void runWeaponKeys(Registry& registry, const InputBindings& bindings)
359{
360 const bool* const kKeys = SDL_GetKeyboardState(nullptr);
361 const SDL_MouseButtonFlags mouse = SDL_GetMouseState(nullptr, nullptr);
362 const bool abilityMenuHeld = bindings.pressed(Action::AbilityMenu, kKeys, mouse);
363 const bool shootDown = bindings.pressed(Action::Shoot, kKeys, mouse);
364 const bool scopeDown = bindings.pressed(Action::Scope, kKeys, mouse);
365 const bool selectLeftNow = abilityMenuHeld && shootDown;
366 const bool selectRightNow = abilityMenuHeld && scopeDown;
367 const bool selectLeftEdge = selectLeftNow && !prevAbilitySelectLeft;
368 const bool selectRightEdge = selectRightNow && !prevAbilitySelectRight;
369 prevAbilitySelectLeft = selectLeftNow;
370 prevAbilitySelectRight = selectRightNow;
371 if (selectLeftEdge)
373 if (selectRightEdge)
375
376 // Grenade: discrete keys. Press CycleGrenade (H) to advance to the next
377 // grenade type with ammo; press ThrowGrenade (G) to throw the selected one.
378 // Both are edge-triggered (rising edge only) and latched here, pulsed once
379 // per frame by the game loop so the server applies each exactly once.
380 const bool grenadeCycleNow = bindings.pressed(Action::CycleGrenade, kKeys, mouse);
381 if (grenadeCycleNow && !prevGrenadeCycleKey) {
383 }
384 prevGrenadeCycleKey = grenadeCycleNow;
385
386 const bool grenadeThrowNow = bindings.pressed(Action::ThrowGrenade, kKeys, mouse);
387 if (grenadeThrowNow && !prevGrenadeThrowKey) {
388 pendingGrenadeThrow = true;
389 }
390 prevGrenadeThrowKey = grenadeThrowNow;
391
392 // Suppress fire/aim while the ability menu is held (Shoot/Scope are
393 // repurposed for ability selection) or while the emote wheel is open.
394 const bool fireSuppressed = abilityMenuHeld || emoteWheelOpen;
395
396 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
397 snap.shooting = shootDown && !fireSuppressed;
398 snap.scoped = scopeDown && !fireSuppressed;
399 snap.switchToPrimary = bindings.pressed(Action::SwitchToPrimary, kKeys, mouse);
400 snap.switchToSecondary = bindings.pressed(Action::SwitchToSecondary, kKeys, mouse);
401 snap.reload = bindings.pressed(Action::Reload, kKeys, mouse);
402 snap.pickup = bindings.pressed(Action::Pickup, kKeys, mouse);
403 snap.abilitySelectHeld = abilityMenuHeld;
404 snap.abilitySelectLeft = false;
405 snap.abilitySelectRight = false;
406 });
407}
408
412inline void runInputSample(
413 Registry& registry,
414 const InputBindings& bindings,
415 float mouseSensitivity = user_settings::kDefaultMouseSensitivity)
416{
417 runMouseLook(registry, mouseSensitivity);
418 runMovementKeys(registry, bindings);
419 runWeaponKeys(registry, bindings);
420}
421
422// ─── Gamepad samplers ────────────────────────────────────────────────────────
423//
424// Mirror the keyboard/mouse pipeline so a controller can drive every action a
425// PC player has access to. SDL3's gamepad API gives us a standardised
426// abstract layout (Xbox 360 / One, DualShock, Switch Pro, etc. all map onto
427// the same buttons/axes) so a single mapping serves "all controllers behave
428// the same way". Tested on Xbox 360.
429//
430// Gamepad samplers OR their state into the InputSnapshot fields that the
431// keyboard samplers already populated, so a player can use kbm and pad
432// simultaneously without one stomping the other. The look sampler ADDs to
433// yaw/pitch the same way mouse delta does, so the two compose cleanly.
434
435namespace gamepad
436{
437
443inline constexpr float k_stickDeadzone = 0.15f;
444
449inline constexpr float k_triggerThreshold = 0.5f;
450
455inline float normaliseAxis(Sint16 raw, float deadzone = k_stickDeadzone)
456{
457 // SDL_Gamepad axes are int16; normalise to [-1, 1]. -32768 is one larger
458 // in magnitude than 32767 — divide by 32767 and clamp to keep the range
459 // symmetric (otherwise full-down on a stick reads as -1.0000305...).
460 const float v = std::clamp(static_cast<float>(raw) / 32767.0f, -1.0f, 1.0f);
461 if (std::fabs(v) < deadzone)
462 return 0.0f;
463 // Rescale [deadzone, 1] → [0, 1] so the user gets the full output range.
464 const float sign = v < 0.0f ? -1.0f : 1.0f;
465 return sign * (std::fabs(v) - deadzone) / (1.0f - deadzone);
466}
467
468} // namespace gamepad
469
487inline void runGamepadLook(Registry& registry,
488 SDL_Gamepad* gamepad,
489 float pitchSensitivity,
490 float yawSensitivity,
491 float deadzone,
492 float dt,
493 bool gravityFlipped = false,
494 bool swapSticks = false)
495{
497 gamepadLookAccel = 0.0f;
498 return;
499 }
500
501 // While the emote wheel is open the right stick selects a sector, so don't
502 // also turn the camera with it.
503 if (emoteWheelOpen) {
504 gamepadLookAccel = 0.0f;
505 return;
506 }
507
508 JoystickAxis lookAxis = getLookJoystickAxes(swapSticks);
509
510 float rx = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, lookAxis.x), deadzone);
511 float ry = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, lookAxis.y), deadzone);
512
513 if (rx == 0.0f && ry == 0.0f) {
514 gamepadLookAccel = 0.0f; // stick centred — drop back to the base turn rate.
515 return;
516 }
517
518 if (gravityFlipped) {
519 rx = -rx;
520 ry = -ry;
521 }
522
523 // COD-style view acceleration: while the look stick is held near full
524 // deflection the turn rate ramps from 1x up to (1 + k_accelMaxBoost)x over
525 // k_accelRampTime seconds, then snaps back to 1x once the stick eases off.
526 // This keeps the (now 30%-lower) base sensitivity precise for small
527 // corrections while still allowing fast full-stick spins. Tune the three
528 // constants to taste.
529 constexpr float k_accelEngageMag = 0.85f;
530 constexpr float k_accelRampTime = 0.5f;
531 constexpr float k_accelMaxBoost = 1.0f;
532 const float stickMag = std::max(std::fabs(rx), std::fabs(ry));
533 if (stickMag >= k_accelEngageMag)
534 gamepadLookAccel = std::min(1.0f, gamepadLookAccel + dt / k_accelRampTime);
535 else
536 gamepadLookAccel = 0.0f;
537 const float accelMult = 1.0f + k_accelMaxBoost * gamepadLookAccel;
538
539 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
540 // Sign convention matches runMouseLook: stick-right (positive rx)
541 // should look right, which means yaw decreases (see runMouseLook
542 // comment for why the negation is correct).
543 snap.yaw -= rx * yawSensitivity * dt * accelMult;
544 snap.yaw = std::remainder(snap.yaw, glm::radians(360.0f));
545
546 // SDL gamepad Y axis is +down/-up (screen-coord convention) — same as
547 // mouse mdy — so adding directly matches mouse-down = pitch+ behaviour.
548 snap.pitch =
549 std::clamp(snap.pitch + ry * pitchSensitivity * dt * accelMult, -glm::radians(89.0f), glm::radians(89.0f));
550 });
551}
552
559inline void runGamepadEmoteWheel(SDL_Gamepad* gamepad, const InputBindings& bindings, bool swapSticks = false)
560{
562 return;
563
564 const bool padNow = bindings.controllerPressed(Action::Emote, gamepad);
565 if (padNow && !prevGamepadEmoteKey) {
566 emoteWheelDirX = 0.0f;
567 emoteWheelDirY = 0.0f;
569 } else if (!padNow && prevGamepadEmoteKey) {
570 if (emoteWheelSelection >= 0)
573 }
574 prevGamepadEmoteKey = padNow;
575
576 if (padNow) {
577 emoteWheelOpen = true;
578 // Right stick gives an absolute direction; scale into the pixel-space
579 // units emoteWheelApplyPointing expects so the deadzone lines up.
580 const JoystickAxis lookAxis = getLookJoystickAxes(swapSticks);
581 const float rx = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, lookAxis.x));
582 const float ry = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, lookAxis.y));
583 emoteWheelApplyPointing(rx * 200.0f, ry * 200.0f, /*accumulate=*/false);
584 }
585}
586
604inline void runGamepadMovement(Registry& registry,
605 SDL_Gamepad* gamepad,
606 const InputBindings& bindings,
607 float deadzone,
608 bool gravityFlipped = false,
609 bool swapSticks = false)
610{
612 return;
613
614 JoystickAxis moveAxis = getMoveJoystickAxes(swapSticks);
615
616 const float lx = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, moveAxis.x), deadzone);
617 const float ly = gamepad::normaliseAxis(SDL_GetGamepadAxis(gamepad, moveAxis.y), deadzone);
618
619 // Movement booleans are derived from a stronger threshold than the deadzone
620 // so a player resting their thumb on the stick doesn't drift-walk. 0.3 is
621 // ~tip pressure on a 360 stick.
622 constexpr float moveThresh = 0.3f;
623 const bool padForward = ly < -moveThresh; // stick-up = -Y in SDL
624 const bool padBack = ly > moveThresh;
625 // Swap left/right when gravity is flipped (same reasoning as keyboard).
626 const bool padLeft = gravityFlipped ? (lx > moveThresh) : (lx < -moveThresh);
627 const bool padRight = gravityFlipped ? (lx < -moveThresh) : (lx > moveThresh);
628
629 const bool padJump = bindings.controllerPressed(Action::Jump, gamepad);
630 const bool padCrouch = bindings.controllerPressed(Action::Crouch, gamepad);
631 const bool padAbility1 = bindings.controllerPressed(Action::Ability1, gamepad);
632 const bool padAbility2 = bindings.controllerPressed(Action::Ability2, gamepad);
633
634 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
635 snap.forward |= padForward;
636 snap.back |= padBack;
637 snap.left |= padLeft;
638 snap.right |= padRight;
639 snap.jump |= padJump;
640 snap.crouch |= padCrouch;
641 snap.ability1 |= padAbility1;
642 snap.ability2 |= padAbility2;
643 });
644}
645
656inline void runGamepadWeapon(Registry& registry, SDL_Gamepad* gamepad, const InputBindings& bindings)
657{
659 return;
660
661 const bool abilityMenuHeld = bindings.controllerPressed(Action::AbilityMenu, gamepad);
662 const bool padShoot = bindings.controllerPressed(Action::Shoot, gamepad);
663 const bool padScope = bindings.controllerPressed(Action::Scope, gamepad);
664 const bool padReload = bindings.controllerPressed(Action::Reload, gamepad);
665 const bool padPrimary = bindings.controllerPressed(Action::SwitchToPrimary, gamepad);
666 const bool padSecondary = bindings.controllerPressed(Action::SwitchToSecondary, gamepad);
667 const bool selectLeftNow = abilityMenuHeld && padShoot;
668 const bool selectRightNow = abilityMenuHeld && padScope;
669 const bool selectLeftEdge = selectLeftNow && !prevGamepadAbilitySelectLeft;
670 const bool selectRightEdge = selectRightNow && !prevGamepadAbilitySelectRight;
671 prevGamepadAbilitySelectLeft = selectLeftNow;
672 prevGamepadAbilitySelectRight = selectRightNow;
673 if (selectLeftEdge)
675 if (selectRightEdge)
677
678 // Grenade: discrete buttons mirroring the keyboard path. D-pad Left cycles
679 // to the next grenade type with ammo; B (East) throws the selected one.
680 // Both edge-triggered and latched into the same once-per-frame pulse.
681 const bool padCycleGrenade = bindings.controllerPressed(Action::CycleGrenade, gamepad);
682 if (padCycleGrenade && !prevGamepadGrenadeCycleKey) {
684 }
685 prevGamepadGrenadeCycleKey = padCycleGrenade;
686
687 const bool padThrowGrenade = bindings.controllerPressed(Action::ThrowGrenade, gamepad);
688 if (padThrowGrenade && !prevGamepadGrenadeThrowKey) {
689 pendingGrenadeThrow = true;
690 }
691 prevGamepadGrenadeThrowKey = padThrowGrenade;
692
693 // Y (Pickup binding): tap = swap weapon, hold = pick up. A press shorter than
694 // k_pickupHoldMs is treated as a tap and latches a weapon swap on release;
695 // holding past the threshold sets the pickup flag (and suppresses the swap).
696 constexpr Uint64 k_pickupHoldMs = 200;
697 const bool padPickupBtn = bindings.controllerPressed(Action::Pickup, gamepad);
698 bool padPickupHeld = false;
699 const Uint64 nowMs = SDL_GetTicks();
700 if (padPickupBtn && !prevGamepadPickupKey) {
701 gamepadPickupDownMs = nowMs;
703 }
704 if (padPickupBtn) {
705 if (nowMs - gamepadPickupDownMs >= k_pickupHoldMs) {
706 padPickupHeld = true;
708 }
710 // Released before the hold threshold → quick tap → swap weapon.
712 }
713 prevGamepadPickupKey = padPickupBtn;
714
715 // Suppress fire/aim while the ability menu modifier is held.
716 const bool fireSuppressed = abilityMenuHeld;
717
718 registry.view<InputSnapshot, LocalPlayer, Controllable>().each([&](InputSnapshot& snap) {
719 snap.shooting |= padShoot && !fireSuppressed;
720 snap.scoped |= padScope && !fireSuppressed;
721 snap.reload |= padReload;
722 snap.pickup |= padPickupHeld;
723 snap.switchToPrimary |= padPrimary;
724 snap.switchToSecondary |= padSecondary;
725 snap.abilitySelectHeld |= abilityMenuHeld;
726 });
727}
728
733inline void runGamepadDeadInput(Registry& registry, SDL_Gamepad* gamepad, const InputBindings& bindings)
734{
736 return;
737 const bool skipRespawn = bindings.controllerPressed(Action::Jump, gamepad);
738 // OR into the flag the keyboard path (runDeadInput) just set — the gamepad
739 // sampler runs right after it, so an assignment here would stomp a Space
740 // press on keyboard whenever a pad is connected. Mirrors how the alive-path
741 // gamepad inputs compose with |= rather than overwriting.
742 registry.view<InputSnapshot, LocalPlayer, RespawnTimer>().each(
743 [&](InputSnapshot& snap, const RespawnTimer& /*unused*/) { snap.skipRespawn |= skipRespawn; });
744}
745
746} // namespace systems
Client side Tag component — entity is eligible to receive player input.
Neutral catalog of selectable emotes (no animation/render deps).
@ Back
Move backward.
Definition InputBindings.hpp:18
@ Jump
Jump and skip respawn while dead.
Definition InputBindings.hpp:21
@ SwitchToSecondary
Switch to the secondary weapon slot.
Definition InputBindings.hpp:31
@ AbilityMenu
Open the ability selection radial menu.
Definition InputBindings.hpp:23
@ Crouch
Crouch and slide.
Definition InputBindings.hpp:22
@ Emote
Hold to open the emote wheel; release to play the selected emote.
Definition InputBindings.hpp:38
@ Reload
Reload the equipped weapon.
Definition InputBindings.hpp:28
@ Scope
Aim/scope the equipped weapon.
Definition InputBindings.hpp:27
@ Forward
Move forward.
Definition InputBindings.hpp:17
@ Ability1
Activate the first ability slot.
Definition InputBindings.hpp:24
@ Ability2
Activate the second ability slot.
Definition InputBindings.hpp:25
@ Right
Strafe right.
Definition InputBindings.hpp:20
@ Left
Strafe left.
Definition InputBindings.hpp:19
@ Pickup
Pick up an interactable weapon or item.
Definition InputBindings.hpp:29
@ CycleGrenade
Cycle to the next grenade type that has ammo.
Definition InputBindings.hpp:34
@ ThrowGrenade
Throw the currently selected grenade.
Definition InputBindings.hpp:35
@ KillSelf
Request self-elimination.
Definition InputBindings.hpp:36
@ SwitchToPrimary
Switch to the primary weapon slot.
Definition InputBindings.hpp:30
@ Shoot
Fire the equipped weapon.
Definition InputBindings.hpp:26
Per-tick player input snapshot for networking and prediction.
Marker component identifying the locally controlled player entity.
Shared ECS registry type alias for the game engine.
entt::registry Registry
Shared ECS registry type alias.
Definition Registry.hpp:11
Component for tracking player respawn times.
Stores action-to-input bindings and helper conversions for UI and persistence.
Definition InputBindings.hpp:128
bool pressed(Action a, const bool *keyStates, SDL_MouseButtonFlags mouseState) const
Test whether an action is currently pressed using SDL keyboard and mouse state.
Definition InputBindings.cpp:261
bool controllerPressed(Action a, SDL_Gamepad *gamepad) const
Test whether an action is currently pressed on a controller.
Definition InputBindings.cpp:272
constexpr int kEmoteCount
Number of emotes shown on the wheel.
Definition EmoteCatalog.hpp:17
Definition InputSampleSystem.hpp:436
constexpr float k_stickDeadzone
Stick deadzone as a fraction of full deflection.
Definition InputSampleSystem.hpp:443
float normaliseAxis(Sint16 raw, float deadzone=k_stickDeadzone)
Convert SDL's int16 axis value to a deadzoned float in [-1, 1].
Definition InputSampleSystem.hpp:455
constexpr float k_triggerThreshold
Trigger threshold for treating an analog trigger as "pressed".
Definition InputSampleSystem.hpp:449
Client-only input sampling system — split into two halves so mouse look can run every iterate() (smoo...
Definition DebugUI.hpp:15
void runGamepadLook(Registry &registry, SDL_Gamepad *gamepad, float pitchSensitivity, float yawSensitivity, float deadzone, float dt, bool gravityFlipped=false, bool swapSticks=false)
Sample the right stick into yaw / pitch.
Definition InputSampleSystem.hpp:487
bool consumePendingGrenadeCycleNext()
Consume and clear the queued "cycle to next grenade" request.
Definition InputSampleSystem.hpp:124
bool prevGamepadGrenadeCycleKey
Gamepad equivalents: CycleGrenade (D-pad Left) / ThrowGrenade (B).
Definition InputSampleSystem.hpp:35
void runInputSample(Registry &registry, const InputBindings &bindings, float mouseSensitivity=user_settings::kDefaultMouseSensitivity)
Legacy combined sampler — calls both runMouseLook and runMovementKeys.
Definition InputSampleSystem.hpp:412
bool pendingGrenadeCyclePrev
Definition InputSampleSystem.hpp:41
void runGamepadDeadInput(Registry &registry, SDL_Gamepad *gamepad, const InputBindings &bindings)
Sample controller skip-respawn input while the local player is dead.
Definition InputSampleSystem.hpp:733
bool prevGamepadEmoteKey
Definition InputSampleSystem.hpp:82
void runGamepadMovement(Registry &registry, SDL_Gamepad *gamepad, const InputBindings &bindings, float deadzone, bool gravityFlipped=false, bool swapSticks=false)
Sample gamepad buttons / left stick into the movement flags.
Definition InputSampleSystem.hpp:604
bool consumePendingGrenadeThrow()
Consume and clear the queued grenade throw request.
Definition InputSampleSystem.hpp:116
int pendingEmoteRequest
Latched emote to play on wheel release; consumed once by the game loop.
Definition InputSampleSystem.hpp:79
bool consumePendingGrenadeCyclePrev()
Consume and clear the queued "cycle to previous grenade" request.
Definition InputSampleSystem.hpp:132
bool prevEmoteKey
Previous-frame Emote-binding state for open/close edge detection.
Definition InputSampleSystem.hpp:81
float emoteWheelDirX
Accumulated pointing direction used to resolve the highlighted sector.
Definition InputSampleSystem.hpp:76
JoystickAxis getMoveJoystickAxes(bool swapSticks)
Return the SDL axes used for movement, honoring the stick-swap setting.
Definition InputSampleSystem.hpp:101
bool prevGamepadAbilitySelectRight
Tracks previous-frame gamepad right ability-select chord for edge detection.
Definition InputSampleSystem.hpp:52
bool pendingGrenadeCycleNext
Latched cycle next/prev requests, consumed once per frame by the game loop.
Definition InputSampleSystem.hpp:40
void runMouseLook(Registry &registry, float mouseSensitivity, bool gravityFlipped=false)
Sample mouse delta and accumulate into yaw / pitch.
Definition InputSampleSystem.hpp:252
void runMovementKeys(Registry &registry, const InputBindings &bindings, bool gravityFlipped=false)
Sample keyboard state into the movement flags.
Definition InputSampleSystem.hpp:298
bool gamepadPickupHoldFired
Set once the press crossed the hold threshold (suppresses tap-swap).
Definition InputSampleSystem.hpp:56
bool prevGamepadAbilitySelectLeft
Tracks previous-frame gamepad left ability-select chord for edge detection.
Definition InputSampleSystem.hpp:50
Uint64 gamepadPickupDownMs
SDL_GetTicks at the Y press, for hold-duration timing.
Definition InputSampleSystem.hpp:55
JoystickAxis getLookJoystickAxes(bool swapSticks)
Return the SDL axes used for camera look, honoring the stick-swap setting.
Definition InputSampleSystem.hpp:92
bool prevGamepadPickupKey
Gamepad Y (Pickup binding) tap-vs-hold state: tap swaps weapon, hold picks up.
Definition InputSampleSystem.hpp:54
bool prevGamepadGrenadeThrowKey
Definition InputSampleSystem.hpp:36
void runEmoteWheelKey(const InputBindings &bindings)
Track the Emote binding (keyboard/mouse) to open/close the wheel.
Definition InputSampleSystem.hpp:217
void runGamepadWeapon(Registry &registry, SDL_Gamepad *gamepad, const InputBindings &bindings)
Sample gamepad buttons / right trigger into the weapon flags.
Definition InputSampleSystem.hpp:656
int consumePendingEmote()
Consume and clear the latched emote-play request (-1 = none).
Definition InputSampleSystem.hpp:148
bool emoteWheelOpen
True while the emote wheel is open (Emote binding held on any device).
Definition InputSampleSystem.hpp:72
bool consumePendingAbilitySelectRight()
Consume and clear the queued right ability-choice request.
Definition InputSampleSystem.hpp:164
bool consumePendingGamepadWeaponSwap()
Consume and clear the latched tap-Y gamepad weapon swap request.
Definition InputSampleSystem.hpp:140
bool gamepadConnected(SDL_Gamepad *gamepad)
True when a gamepad handle is non-null and still connected.
Definition InputSampleSystem.hpp:110
bool pendingGrenadeThrow
Latched throw request, consumed once per frame by the game loop.
Definition InputSampleSystem.hpp:38
bool prevAbilitySelectRight
Tracks previous-frame Alt+RMB state for ability choice edge detection.
Definition InputSampleSystem.hpp:45
void runWeaponKeys(Registry &registry, const InputBindings &bindings)
Sample keyboard state into the weapon flags.
Definition InputSampleSystem.hpp:358
bool pendingGamepadWeaponSwap
Latched tap-Y weapon swap, consumed once per frame by the game loop.
Definition InputSampleSystem.hpp:58
void runGamepadEmoteWheel(SDL_Gamepad *gamepad, const InputBindings &bindings, bool swapSticks=false)
Track the Emote binding (controller) to open/close the wheel and pick a sector from the right stick.
Definition InputSampleSystem.hpp:559
bool prevAbilitySelectLeft
Tracks previous-frame Alt+LMB state for ability choice edge detection.
Definition InputSampleSystem.hpp:43
int emoteWheelSelection
Currently highlighted emote sector while the wheel is open, or -1 (none).
Definition InputSampleSystem.hpp:74
bool pendingAbilitySelectLeft
Latched ability choice requests, consumed once per sent input batch.
Definition InputSampleSystem.hpp:47
bool pendingAbilitySelectRight
Definition InputSampleSystem.hpp:48
float gamepadLookAccel
COD-style look acceleration progress in [0, 1]; ramps while the look stick is held near full deflecti...
Definition InputSampleSystem.hpp:61
bool prevGrenadeThrowKey
Definition InputSampleSystem.hpp:33
bool prevKillSelfKey
Tracks previous-frame key state for edge detection.
Definition InputSampleSystem.hpp:30
bool prevGrenadeCycleKey
Previous-frame CycleGrenade (H) / ThrowGrenade (G) key state for edge detection (keyboard/mouse).
Definition InputSampleSystem.hpp:32
bool consumePendingAbilitySelectLeft()
Consume and clear the queued left ability-choice request.
Definition InputSampleSystem.hpp:156
void runDeadInput(Registry &registry, const InputBindings &bindings)
Sample skip-respawn input while the local player is dead.
Definition InputSampleSystem.hpp:339
float emoteWheelDirY
Definition InputSampleSystem.hpp:77
void emoteWheelApplyPointing(float dx, float dy, bool accumulate)
Resolve emoteWheelSelection from a pointing input.
Definition InputSampleSystem.hpp:179
constexpr float kDefaultMouseSensitivity
Mouse-look sensitivity in radians per SDL relative mouse unit.
Definition UserSettings.hpp:15
Client side Tag component — entity is eligible to receive player input.
Definition Controllable.hpp:9
One tick of player input, stamped with the tick it was sampled on.
Definition InputSnapshot.hpp:17
bool switchToSecondary
Switch to gun in secondary slot.
Definition InputSnapshot.hpp:34
bool pickup
Pick Up button (f).
Definition InputSnapshot.hpp:32
bool killSelf
Debug: kill self (rising-edge only).
Definition InputSnapshot.hpp:40
bool switchToPrimary
Switch to gun in primary slot.
Definition InputSnapshot.hpp:33
bool jump
Space key.
Definition InputSnapshot.hpp:25
bool ability2
Activate ability 2.
Definition InputSnapshot.hpp:50
bool abilitySelectRight
Choose the right pending ability option (edge-triggered).
Definition InputSnapshot.hpp:53
bool ability1
Activate ability 1.
Definition InputSnapshot.hpp:49
bool abilitySelectLeft
Choose the left pending ability option (edge-triggered).
Definition InputSnapshot.hpp:52
bool skipRespawn
Skip respawn timer (space while dead).
Definition InputSnapshot.hpp:41
bool right
D key.
Definition InputSnapshot.hpp:24
bool forward
W key.
Definition InputSnapshot.hpp:21
bool left
A key.
Definition InputSnapshot.hpp:23
bool reload
Reload button.
Definition InputSnapshot.hpp:31
float yaw
Horizontal look angle in radians (accumulated from mouse X deltas).
Definition InputSnapshot.hpp:56
bool scoped
Right click.
Definition InputSnapshot.hpp:30
bool crouch
Left Ctrl key.
Definition InputSnapshot.hpp:26
bool shooting
Primary fire button.
Definition InputSnapshot.hpp:29
float pitch
Vertical look angle in radians, clamped to [-89°, +89°] by InputSampleSystem.
Definition InputSnapshot.hpp:57
bool back
S key.
Definition InputSnapshot.hpp:22
bool abilitySelectHeld
True while holding the ability-selection modifier.
Definition InputSnapshot.hpp:51
Marker component that tags exactly one entity per client as the locally controlled player.
Definition LocalPlayer.hpp:12
ECS component: countdown until a dead player respawns.
Definition RespawnTimer.hpp:10
Gamepad axis mapping configuration for look and move axes, used to support stick swapping in user set...
Definition InputSampleSystem.hpp:86
SDL_GamepadAxis x
Definition InputSampleSystem.hpp:87
SDL_GamepadAxis y
Definition InputSampleSystem.hpp:88