92 lines
4.5 KiB
Lua
Executable File
92 lines
4.5 KiB
Lua
Executable File
local M = dofile(assert(arg[1], "movement.lua path required"))
|
|
local total = 0
|
|
local function test(name, fn)
|
|
local ok, err = pcall(fn)
|
|
assert(ok, name .. ": " .. tostring(err)); total = total + 1
|
|
end
|
|
local function fixture(stamina)
|
|
return {}, {stamina = stamina or 100, food = 80, water = 80, wet = 0},
|
|
{aux1 = true, up = true}, {grounded = true, velocity = {x = 3, y = 0, z = 0}}
|
|
end
|
|
local function near(a, b) assert(math.abs(a - b) < 0.00001, tostring(a) .. " ~= " .. tostring(b)) end
|
|
test("zero negative and invalid dt cannot change speed or stamina", function()
|
|
local s, state, c, e = fixture()
|
|
for _, dt in ipairs({0, -1, 0 / 0}) do
|
|
local r = M.step(s, state, c, dt, e); near(r.speed, 1); near(state.stamina, 100)
|
|
end
|
|
end)
|
|
test("stall clamps speed and stamina without replay", function()
|
|
local s, state, c, e = fixture()
|
|
local r = M.step(s, state, c, 100, e)
|
|
near(r.speed, 1.3); near(state.stamina, 98.7)
|
|
end)
|
|
test("speed smoothly reaches target without overshoot", function()
|
|
local s, state, c, e = fixture()
|
|
local r = M.step(s, state, c, 0.05, e); near(r.speed, 1.15)
|
|
for _ = 1, 8 do r = M.step(s, state, c, 0.05, e); assert(r.speed <= 1.45) end
|
|
near(r.speed, 1.45)
|
|
c.aux1 = false; r = M.step(s, state, c, 0.05, e); near(r.speed, 1.3)
|
|
end)
|
|
test("exhaustion cannot stutter while aux remains held", function()
|
|
local s, state, c, e = fixture(8.5)
|
|
assert(not M.step(s, state, c, 0.1, e).sprinting and s.exhausted)
|
|
for _ = 1, 90 do assert(not M.step(s, state, c, 0.1, e).sprinting) end
|
|
assert(state.stamina >= 30)
|
|
c.aux1 = false; M.step(s, state, c, 0.1, e)
|
|
c.aux1 = true; assert(M.step(s, state, c, 0.1, e).sprinting)
|
|
end)
|
|
test("release remembers intent but still requires thirty reserve", function()
|
|
local s, state, c, e = fixture(8)
|
|
M.step(s, state, c, 0, e); c.aux1 = false; M.step(s, state, c, 0.1, e)
|
|
c.aux1 = true; state.stamina = 29.99
|
|
assert(not M.step(s, state, c, 0, e).sprinting)
|
|
state.stamina = 30; assert(M.step(s, state, c, 0, e).sprinting)
|
|
-- A release at zero must survive subsequent held frames below the threshold.
|
|
s, state, c, e = fixture(0); c.aux1 = false; M.step(s, state, c, 0.1, e)
|
|
c.aux1 = true
|
|
for _ = 1, 34 do M.step(s, state, c, 0.1, e) end
|
|
assert(M.step(s, state, c, 0, e).sprinting)
|
|
end)
|
|
test("backward diagonal backwards and sneak never sprint", function()
|
|
local s, state, c, e = fixture()
|
|
c.down = true; assert(not M.step(s, state, c, 0.1, e).sprinting)
|
|
c.down, c.up = false, false; assert(not M.step(s, state, c, 0.1, e).sprinting)
|
|
c.up, c.sneak = true, true; assert(not M.step(s, state, c, 0.1, e).sprinting)
|
|
end)
|
|
test("spear brace requires wield and takes priority over sprint", function()
|
|
local s, state, c, e = fixture(); c.RMB = true; e.weapon = "hearth:spear"
|
|
local r = M.step(s, state, c, 0.1, e)
|
|
assert(r.blocking and not r.sprinting); near(r.speed, 0.7)
|
|
r = M.step(s, state, c, 0.1, e); near(r.speed, 0.6)
|
|
e.weapon = "hearth:knife"; r = M.step(s, state, c, 0.1, e)
|
|
assert(not r.blocking and r.sprinting)
|
|
end)
|
|
test("real vertical velocity and support determine grounded", function()
|
|
local s, state, c, e = fixture(); e.velocity.y = 4
|
|
assert(not M.step(s, state, c, 0.1, e).grounded)
|
|
e.velocity.y = -4; assert(not M.step(s, state, c, 0.1, e).sprinting)
|
|
e.velocity.y, e.grounded = 0, false; assert(not M.step(s, state, c, 0.1, e).sprinting)
|
|
end)
|
|
test("wet speed does not change wetness or other survival values", function()
|
|
local s, state, c, e = fixture(); state.wet = 75; c.aux1 = false
|
|
local r = M.step(s, state, c, 0.1, e); near(r.speed, 0.95)
|
|
assert(state.wet == 75 and state.food == 80 and state.water == 80)
|
|
end)
|
|
test("swimming forbids sprint brace and uses swim pace", function()
|
|
local s, state, c, e = fixture(); e.swimming, e.weapon, c.RMB = true, "hearth:spear", true
|
|
local r = M.step(s, state, c, 0.1, e)
|
|
assert(r.swimming and not r.sprinting and not r.blocking); near(r.speed, 0.75)
|
|
end)
|
|
test("distance motion is velocity derived not a held direction", function()
|
|
local s, state, c, e = fixture(); e.velocity.x = 0
|
|
assert(not M.step(s, state, c, 0.1, e).moving)
|
|
e.velocity.z = 1; c.up = false; assert(M.step(s, state, c, 0.1, e).moving)
|
|
end)
|
|
test("recovery stays bounded and respects depleted needs", function()
|
|
local s, state, c, e = fixture(50); c.aux1 = false; state.food = 0
|
|
M.step(s, state, c, 0.1, e); near(state.stamina, 50.2)
|
|
state.stamina, state.food = 99.9, 80; M.step(s, state, c, 0.1, e); near(state.stamina, 100)
|
|
end)
|
|
assert(total == 12, "Unexpected movement regression count")
|
|
print("PASS " .. total .. " movement tests")
|