feat(vagrant): sprint (shift+wasd) + backpack interaction
Sprint: holding shift while WASD doubles player_speed (120 → 240) and walk-anim play-speed (0.45 → 0.9). Mid-walk sprint toggle uses puppet v0.4.4 set_play_speed → no cycle restart. Releasing shift or stopping WASD reverts to normal speed in next frame. Interaction: 'interact' bound to E. Proximity trigger registered on the backpack sprite at (350, 400) with range 40px. Pressing E within range fires callback that prints a log message with the actor distance. Uses existing lib-core.interaction (registered at init, polled by interaction.update(player.x, player.y) each frame). New dep: lib-core.interaction ^0.1.0. Module version 0.4.0 → 0.4.4 (syncs with puppet bump).
This commit is contained in:
63
init.lua
63
init.lua
@@ -1,20 +1,31 @@
|
||||
-- sporel-module-vagrant-skeleton v0.4.0
|
||||
-- sporel-module-vagrant-skeleton v0.4.4
|
||||
-- Sprite-mode test-chamber: 13-bone humanoid puppet + sprite-tilemap +
|
||||
-- hardcoded furniture. Subterrain-style puppet control model:
|
||||
-- multi-look-target (aim/soft), slerp body rotation, virtual lower_control bone,
|
||||
-- 4 directional walk animations, HandleAnimationLower port.
|
||||
-- multi-look-target (aim/soft), slerp body rotation, hip-static legs,
|
||||
-- foot-body-orientation. Sprint (shift+wasd) + interaction (E near
|
||||
-- backpack) added v0.4.4.
|
||||
|
||||
local input = require("lib-core.input")
|
||||
local camera = require("lib-core.camera")
|
||||
local r_lib = require("lib-core.render")
|
||||
local maps = require("lib-core.maps")
|
||||
local puppet = require("lib-core.puppet")
|
||||
local interaction = require("lib-core.interaction")
|
||||
|
||||
local M = {}
|
||||
|
||||
-- Walk + sprint speed tunings.
|
||||
local WALK_SPEED_NORMAL = 120
|
||||
local WALK_SPEED_SPRINT = 240 -- 2x
|
||||
local ANIM_SPEED_NORMAL = 0.45
|
||||
local ANIM_SPEED_SPRINT = 0.9 -- 2x
|
||||
|
||||
-- Furniture positions (matches M.draw hardcoded sprites).
|
||||
local BACKPACK_X, BACKPACK_Y = 350, 400
|
||||
local INTERACTION_RANGE = 40 -- pixels
|
||||
|
||||
local state = {
|
||||
player = nil,
|
||||
player_speed = 120,
|
||||
map = nil,
|
||||
bed_tex = nil,
|
||||
bench_tex = nil,
|
||||
@@ -24,6 +35,7 @@ local state = {
|
||||
lower_control_rot_deg = 0,
|
||||
move_x = 0,
|
||||
move_y = 0,
|
||||
sprinting = false, -- shift+wasd = 2x speed + 2x anim
|
||||
}
|
||||
|
||||
-- CI hook gating (mirrors Phase 1 convention).
|
||||
@@ -113,10 +125,11 @@ local function update_lower_anim(moving, move_x, move_y)
|
||||
end
|
||||
puppet.stop(state.player, "idle_lower")
|
||||
puppet.stop(state.player, "idle")
|
||||
-- Slow walk to 0.4x substrate speed (user pref: still too fast at 0.5).
|
||||
puppet.play(state.player, anim_id, { loop = true, speed = 0.45 })
|
||||
-- Play with current speed (normal or sprint).
|
||||
local anim_speed = state.sprinting and ANIM_SPEED_SPRINT or ANIM_SPEED_NORMAL
|
||||
puppet.play(state.player, anim_id, { loop = true, speed = anim_speed })
|
||||
if not puppet.is_playing(state.player, "walk_upper") then
|
||||
puppet.play(state.player, "walk_upper", { loop = true, speed = 0.45 })
|
||||
puppet.play(state.player, "walk_upper", { loop = true, speed = anim_speed })
|
||||
end
|
||||
state.walk_anim_current = anim_id
|
||||
if CI_FRAME_PERF and ci_last_state ~= "walk" then
|
||||
@@ -187,8 +200,18 @@ function M.init(ctx)
|
||||
input.bind("walk_right", { "d" })
|
||||
input.bind("walk_up", { "w" })
|
||||
input.bind("walk_down", { "s" })
|
||||
input.bind("sprint", { "shift" }) -- held while walking → 2x speed
|
||||
input.bind("interact", { "e" }) -- fires nearest proximity-trigger
|
||||
input.bind("quit_to_launcher", { "escape" })
|
||||
|
||||
-- Proximity trigger on the backpack sprite (drawn at BACKPACK_X/Y in M.draw).
|
||||
-- Range 40px; press E while within that radius to fire the callback.
|
||||
interaction.register(BACKPACK_X, BACKPACK_Y, INTERACTION_RANGE, "interact", function(ctx)
|
||||
engine.print(string.format(
|
||||
"vagrant: interact with backpack (distance=%.1f)",
|
||||
ctx.distance))
|
||||
end)
|
||||
|
||||
-- CI trace: assets loaded count (puppet+tilemap+furniture textures).
|
||||
if CI_FRAME_PERF and not ci_assets_traced then
|
||||
-- 7 puppet textures + 4 tile textures + 3 furniture textures = 14
|
||||
@@ -215,6 +238,13 @@ function M.update(ctx, dt)
|
||||
if input.is_action_down("walk_down") then dy = dy + 1 end
|
||||
local moving = (dx ~= 0 or dy ~= 0)
|
||||
|
||||
-- Sprint = shift held + actually moving. Updated BEFORE move so the
|
||||
-- selected speed matches; also feeds update_lower_anim's anim-speed
|
||||
-- pick at walk-start transitions.
|
||||
local sprint_held = input.is_action_down("sprint")
|
||||
state.sprinting = moving and sprint_held
|
||||
local current_walk_speed = state.sprinting and WALK_SPEED_SPRINT or WALK_SPEED_NORMAL
|
||||
|
||||
-- CI_DRIVE: synthetic scripted move (preserve existing P0-S25b semantics).
|
||||
if CI_DRIVE then
|
||||
ci_drive_frame = ci_drive_frame + 1
|
||||
@@ -223,7 +253,7 @@ function M.update(ctx, dt)
|
||||
moving = true
|
||||
local pos = puppet.position(state.player)
|
||||
puppet.move_to(state.player,
|
||||
pos.x + dx * state.player_speed * dt, pos.y)
|
||||
pos.x + dx * WALK_SPEED_NORMAL * dt, pos.y)
|
||||
else
|
||||
moving = false
|
||||
end
|
||||
@@ -236,8 +266,8 @@ function M.update(ctx, dt)
|
||||
state.move_y = dy / len
|
||||
local pos = puppet.position(state.player)
|
||||
puppet.move_to(state.player,
|
||||
pos.x + state.move_x * state.player_speed * dt,
|
||||
pos.y + state.move_y * state.player_speed * dt)
|
||||
pos.x + state.move_x * current_walk_speed * dt,
|
||||
pos.y + state.move_y * current_walk_speed * dt)
|
||||
elseif not moving then
|
||||
state.move_x = 0
|
||||
state.move_y = 0
|
||||
@@ -251,14 +281,25 @@ function M.update(ctx, dt)
|
||||
|
||||
update_lower_anim(moving, state.move_x, state.move_y)
|
||||
|
||||
-- Sprint toggle mid-walk: adjust play-speed of running anims without
|
||||
-- restarting their cycle (puppet v0.4.4 set_play_speed). Silent no-op
|
||||
-- if anim not currently playing.
|
||||
local anim_speed = state.sprinting and ANIM_SPEED_SPRINT or ANIM_SPEED_NORMAL
|
||||
puppet.set_play_speed(state.player, "walk_fwd_lower", anim_speed)
|
||||
puppet.set_play_speed(state.player, "walk_upper", anim_speed)
|
||||
|
||||
-- Look-targets: head tracks raw mouse (aim), body tracks same for now (soft).
|
||||
local mx, my = engine.input.get_mouse_pos()
|
||||
local wx, wy = camera.screen_to_world(mx, my)
|
||||
puppet.set_look_target(state.player, "aim", wx, wy)
|
||||
puppet.set_look_target(state.player, "soft", wx, wy)
|
||||
|
||||
-- Camera follows puppet position each frame.
|
||||
-- Interaction: dispatches the nearest matching proximity-trigger when
|
||||
-- 'interact' was pressed this frame. Uses player.position as actor.
|
||||
local pos = puppet.position(state.player)
|
||||
interaction.update(pos.x, pos.y)
|
||||
|
||||
-- Camera follows puppet position each frame.
|
||||
camera.set_target(pos.x, pos.y)
|
||||
camera.update(dt)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "vagrant-skeleton",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.4",
|
||||
"kind": "module",
|
||||
"api": "^0.1",
|
||||
"ci_frames": 60,
|
||||
@@ -15,7 +15,8 @@
|
||||
{"id": "lib-core.camera", "version": "0.3.0"},
|
||||
{"id": "lib-core.render", "version": "0.1.0"},
|
||||
{"id": "lib-core.maps", "version": "0.1.2"},
|
||||
{"id": "lib-core.puppet", "version": "0.4.3"},
|
||||
{"id": "lib-core.puppet", "version": "0.4.4"},
|
||||
{"id": "lib-core.interaction", "version": "0.1.0"},
|
||||
{"id": "lib-asset.prototype-subterrain", "version": "0.1.1"}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user