DramaticShapeVoxelMod archival mirror · read-only
08bcbf7629c7874bc27d27001784e6ebc97ecbfa

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.