-- Pure M1 controller. Only ephemeral session fields and state.stamina mutate. -- No engine access: the runtime supplies weapon, velocity, ground and water. local M = {} function M.delta(dt) if type(dt) ~= "number" or dt ~= dt or dt <= 0 then return 0 end return math.min(dt, 0.1) -- never replay stalls as movement/exhaustion end function M.step(session, state, controls, dt, env) controls, env = controls or {}, env or {} dt = M.delta(dt) local velocity = env.velocity or {x = 0, y = 0, z = 0} local grounded = env.grounded == true and math.abs(velocity.y or 0) < 0.2 local swimming = env.swimming == true local moving = (velocity.x or 0)^2 + (velocity.z or 0)^2 > 0.04 if state.stamina <= 8 and not session.exhausted then session.exhausted = true; session.sprint_released = false end if session.exhausted and not controls.aux1 then session.sprint_released = true end if session.exhausted and session.sprint_released and state.stamina >= 30 then session.exhausted, session.sprint_released = false, false end local blocking = env.weapon == "hearth:spear" and controls.RMB == true and not swimming local sprinting = controls.aux1 == true and controls.up == true and not controls.down and not controls.sneak and not blocking and grounded and not swimming and not session.exhausted and state.stamina > 8 -- Keep survival rates identical to model.needs; runtime restores its duplicate -- stamina result but retains that model's sprint hunger/thirst expenditure. local rate = sprinting and -13 or ((state.food > 0 and state.water > 0) and 9 or 2) state.stamina = math.max(0, math.min(100, state.stamina + rate * dt)) if sprinting and state.stamina <= 8 then session.exhausted, session.sprint_released, sprinting = true, false, false end local target = swimming and 0.75 or (blocking and 0.6 or (sprinting and 1.45 or 1)) if not swimming and not blocking and (state.wet or 0) >= 60 then target = target * 0.95 end local speed = session.movement_speed or 1 local change = math.max(-3 * dt, math.min(3 * dt, target - speed)) speed = speed + change session.movement_speed, session.sprinting, session.blocking = speed, sprinting, blocking session.grounded, session.swimming, session.moving = grounded, swimming, moving return {speed = speed, sprinting = sprinting, blocking = blocking, grounded = grounded, swimming = swimming, moving = moving} end return M