DramaticShapeVoxelMod archival mirror · read-only
tests/freemove_probe.lua

freemove_probe.lua

tests/freemove_probe.lua6.4 KB10064447cc62b4b8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
-- Probe: does WASD still walk CAMERA-RELATIVE on the 1ST/3RD rungs?
--
-- The regression report is "in first and third person the wasd keys now
-- move in cardinal directions". Cardinal means the yaw is not being
-- applied -- either FreeMove.tick is not the handler that ran (so the
-- engine's grid walk did, which is cardinal by construction), or it ran
-- and moveWorld got a yaw of zero.
--
-- So measure both: which handler took the frame, what the yaw was, and
-- which way the player actually travelled for a held W.
--
--   POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/freemove_probe.lua \
--   "/c/Program Files/LOVE/lovec.exe" .
return function(game)
  local U = dofile("tests/drivers/util.lua")
  local Pipelines = require("src.render.Pipelines")

  local handle = game.mods.exports["DRAMATIC_SHAPE"]
  if not (handle and handle.lib) then
    U.log("DRAMATIC_SHAPE is not loaded")
    return love.event.quit()
  end
  local V = handle.lib
  local FirstPerson = V.require("FirstPerson")
  local FreeMove = V.require("FreeMove")
  local Voxel = V.require("VoxelState")
  local ChunkMesher = V.require("ChunkMesher")

  require("src.world.OverworldController").rollEncounter = function() return nil end

  -- FirstPerson captures the mouse only with WINDOW FOCUS, and a driver
  -- window never has it -- so without this the look reads as dead for a
  -- reason that has nothing to do with the code under test. Force the
  -- answer it gates on; setRelativeMode then arms and the relative-motion
  -- wrap claims the deltas exactly as it would for a player.
  love.window.hasFocus = function() return true end

  -- count which walk handler actually takes the frames
  local ticks = 0
  local innerTick = FreeMove.tick
  FreeMove.tick = function(...)
    ticks = ticks + 1
    return innerTick(...)
  end

  U.teleport(game, "ROUTE_1", 5, 8, "down")
  U.wait(60)

  local function settle()
    for _ = 1, 900 do
      if ChunkMesher.pending() == 0 then break end
      U.wait(1)
    end
    for _ = 1, 300 do
      if FirstPerson.blend >= 1 and Voxel.ready then break end
      U.wait(1)
    end
    U.wait(30)
  end

  -- W is the UP button; the B button's key is "x" (see Input's
  -- DEFAULT_BINDINGS -- the driver must press KEYS, not button names, to
  -- exercise the real path)
  local function holdKey(k, frames)
    love.keypressed(k, k, false)
    U.wait(frames)
    love.keyreleased(k, k)
    U.wait(4)
  end

  for _, rung in ipairs({ { "1ST", Voxel.FP_LEVEL }, { "3RD", Voxel.TP_LEVEL } }) do
    Pipelines.setLevel("voxel", rung[2])
    settle()
    -- face EAST: yaw is the free-roam look, and a camera-relative W must
    -- then walk +X. A cardinal W walks -Y (north) whatever the camera does.
    FirstPerson.yaw = math.pi / 2
    U.wait(10)

    local ow = game.stack:top()
    local p = ow and ow.player
    if not p then U.log(rung[1] .. ": no player") break end
    local x0, y0 = p.px, p.py
    ticks = 0
    holdKey("w", 40)
    local dx, dy = p.px - x0, p.py - y0
    U.log(("%s: driving=%s freeMove ticks=%d yaw=%.2f  W moved dx=%.1f dy=%.1f")
          :format(rung[1], tostring(FirstPerson.driving()), ticks,
                  FirstPerson.yaw, dx, dy))
    local wx, wz = FirstPerson.moveWorld(0, 1)
    U.log(("%s: moveWorld(0,1) = %.2f,%.2f  (want a mostly-X vector at this yaw)")
          :format(rung[1], wx, wz))

    -- ------- and does the LOOK still turn?
    --
    -- A yaw that never moves is the same symptom from the player's seat:
    -- it stays at the cardinal angle the rung was entered on (FACING_ANGLE
    -- is one of four compass points), so W walks due north for ever and
    -- "wasd moves in cardinal directions" is exactly what it feels like.
    -- The capture mode's pointer wraps are installed OUTSIDE FirstPerson's,
    -- so this is the path that could have regressed.
    -- FirstPerson only claims relative motion while it has CAPTURED the
    -- mouse, and it captures only with window focus. A driver window that
    -- never got focus would show a dead look for a reason that has nothing
    -- to do with the code -- so record the discriminator rather than read
    -- a zero and blame the wrap.
    local okF, focus = pcall(function() return love.window.hasFocus() end)
    local okR, rel = pcall(function() return love.mouse.getRelativeMode() end)
    U.log(("%s: engaged=%s focus=%s relativeMode=%s")
          :format(rung[1], tostring(FirstPerson.engaged()),
                  okF and tostring(focus) or "?",
                  okR and tostring(rel) or "?"))

    local before = FirstPerson.yaw
    for _ = 1, 10 do
      love.mousemoved(400, 300, 12, 0, false)
      U.wait(1)
    end
    U.wait(4)
    U.log(("%s: mouse look -- yaw %.3f -> %.3f (delta %.3f)%s")
          :format(rung[1], before, FirstPerson.yaw, FirstPerson.yaw - before,
                  math.abs(FirstPerson.yaw - before) < 1e-6
                  and "   <-- THE LOOK IS DEAD" or ""))
  end

  -- ------- and now the way a PLAYER gets there: the "3" hotkey
  --
  -- Pipelines.setLevel above is the driver's shortcut. A player cycles the
  -- rung with 3, which goes through the mod's own cycleVoxel. If that
  -- leaves Voxel.level disagreeing with the pipeline's level, the camera
  -- can be first-person while FreeMove.engaged() says no -- and then the
  -- ENGINE's grid handler takes the frame, which walks cardinally. That is
  -- the reported symptom exactly, so it is worth entering the rung the
  -- same way the report did.
  Pipelines.setLevel("voxel", 0)
  U.wait(20)
  for i = 1, 8 do
    love.keypressed("3", "3", false)
    U.wait(3)
    love.keyreleased("3", "3")
    U.wait(12)
    local lvl = Pipelines.level("voxel")
    U.log(("hotkey 3 x%d -> pipeline level=%s  Voxel.level=%s  freeCam=%s "
           .. "engaged=%s driving=%s")
          :format(i, tostring(lvl), tostring(Voxel.level),
                  tostring(Voxel.isFreeCam(Voxel.level)),
                  tostring(FirstPerson.engaged()),
                  tostring(FirstPerson.driving())))
    if Voxel.isFreeCam(Voxel.level) then
      settle()
      local ow = game.stack:top()
      local p = ow and ow.player
      FirstPerson.yaw = math.pi / 2
      U.wait(6)
      local yaw0 = FirstPerson.yaw
      local x0, y0 = p.px, p.py
      ticks = 0
      holdKey("w", 30)
      U.log(("  walked from the HOTKEY rung: ticks=%d yaw=%.2f dx=%.1f dy=%.1f%s")
            :format(ticks, yaw0, p.px - x0, p.py - y0,
                    ticks == 0 and "   <-- ENGINE GRID WALK (cardinal)" or ""))
    end
  end

  FreeMove.tick = innerTick
  U.log("done")
end