Merge branch 'dev' into 3rd-person-camera
DramaticShape · 2026-08-03 ·
parent a3a712205b f5970d8d9a
Merge branch 'dev' into 3rd-person-camera
Changed files
LICENSE | 21 ++
README.md | 6 +-
lib/FirstPerson.lua | 39 ++++
lib/FreeMove.lua | 33 ++-
lib/Horde.lua | 707 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/HordeExitPrompt.lua | 94 +++++++++
lib/HordeGameOver.lua | 107 ++++++++++
lib/HordeGun.lua | 511 +++++++++++++++++++++++++++++++++++++++++++++
lib/HordeHud.lua | 484 ++++++++++++++++++++++++++++++++++++++++++
lib/HordeMobs.lua | 553 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/HordeSfx.lua | 262 +++++++++++++++++++++++
lib/VR.lua | 118 ++++++++++-
lib/VRXR.lua | 21 ++
lib/VoxelScene.lua | 17 ++
main.lua | 58 +++++-
mod.card | 6 +
tests/dramatic_shape_test.lua | 340 ++++++++++++++++++++++++++++++
tests/horde_shots.lua | 329 +++++++++++++++++++++++++++++
18 files changed, 3685 insertions(+), 21 deletions(-)
Diff
diff --cc lib/FreeMove.lua
index cb9f2e8,712b583..bf5f9c4
--- a/lib/FreeMove.lua
+++ b/lib/FreeMove.lua
@@@ -242,14 -233,18 +251,22 @@@ function FreeMove.tick(state
local input = Game.input
-- the head is the facing: what A talks to, what the sun's card shows,
- -- which way a bonk points
- p.facing = FirstPerson.compassFacing()
+ -- which way a bonk points. (A body that is WALKING may turn along its
+ -- travel instead -- see below, once there is a travel to turn along; a
+ -- standing one always faces where the camera looks, which is what makes
+ -- A predictable.) pointBody rather than compassFacing, so the card also
+ -- gets the CONTINUOUS bearing behind that compass point.
+ p.facing = FirstPerson.pointBody(0, 0)
- if input:wasPressed("a") then
+ -- HORDE MODE takes both of these away for as long as it runs: there is
+ -- no pausing (START), and nobody stops to read a sign with the horde
+ -- coming (A, which is also the button the mode's own GAME OVER card
+ -- wants left unspent). Everything below -- the walk, the wall slide and
+ -- the blocked-push verbs, warps included -- keeps working, because the
+ -- crowd has to be able to follow the player through a door.
+ local suppressed = V.require("Horde").suppressWorldInput()
+
+ if not suppressed and input:wasPressed("a") then
state:interact()
return
end
diff --cc main.lua
index a5d2ade,d4abea1..cc062c3
--- a/main.lua
+++ b/main.lua
@@@ -93,8 -92,14 +93,15 @@@ local Water = V.require("Water"
local AntiAlias = V.require("AntiAlias")
local FirstPerson = V.require("FirstPerson")
local FreeMove = V.require("FreeMove")
+local CamControl = V.require("CamControl")
local VR = V.require("VR")
+ -- HORDE MODE: the konami code's minigame. Horde owns the state machine and
+ -- every hook; the other four are the gun, the crowd, the readout and the
+ -- chip-synthesized sounds it fires. See lib/Horde.lua for the whole design.
+ local Horde = V.require("Horde")
+ local HordeGun = V.require("HordeGun")
+ local HordeHud = V.require("HordeHud")
+ local HordeSfx = V.require("HordeSfx")
-- Forward declaration: the voxel pipeline's update hook (registered below)
-- calls this, and it is defined further down with the settings it drives.
@@@ -537,19 -570,18 +572,29 @@@ d
local inner = Game.keypressed
function Game:keypressed(key)
+ -- HORDE MODE owns the keyboard's spare keys while it runs: R reloads,
+ -- and the mode keys are swallowed rather than left to change the rung
+ -- or the post-processing out from under a locked camera.
+ if Horde.active then
+ if key == "r" then
+ HordeGun.reload()
+ return
+ end
+ if HOTKEYS[key] then return end
+ end
local claim = HOTKEYS[key]
local top = self.stack and self.stack:top()
+ -- Q and E work whichever camera is in front of the player -- the
+ -- battle's lens, the third-person boom, or the engine's own survey
+ -- zoom on an orbit rung. CamControl answers which, and answers "none"
+ -- for 1ST and for every screen with no camera of ours behind it, in
+ -- which case the key falls through untouched. Ahead of the hotkey
+ -- table because unlike those it is NOT free-roam only: a staged battle
+ -- is exactly where the zoom is most wanted.
+ if (key == "q" or key == "e")
+ and not (top and top.onKeyPressed) then
+ if CamControl.zoomBy(key == "q" and 1 or -1) then return end
+ end
-- A screen with its own key handler gets the key first, exactly as the
-- engine's first branch does: typing a nickname must not toggle a
-- render mode. Only free-roam presses are ours to take.