Character and movement pass
NPC characters - Articulated rig (hips/knees, shoulders/elbows/hands) replaces rigid capsules; the walk cycle bends knees and elbows and flees with a longer stride. - Layered outfits (jacket/vest/coat/dress), extended accessories and hand items (bag, umbrella, phone, camera, clipboard, cup), face details and five hair silhouettes, all deterministic per profile seed. - Idle behaviour layers (phone, clipboard, crossed arms, look-around), breathing and weight shift; escalation raises hands or points, and noticed NPCs turn toward the guard. - Reaction symbols above the head (!!, ?!, !, ?), door-queue offsets assigned by GameManager, three LOD tiers with hysteresis and a billboard impostor. - smoke_npc_budget.gd: 16 avatars = 534 meshes, ~6.9 ms average (50 ms budget). Player movement - Separate accel/decel, step-up assist for kerbs and the entrance steps, and a forward-axis fix: W was inverted (get_axis(forward, back) is negative). - Head bob, landing dip and precision-walk FOV, gated by persisted head-bob and invert-Y settings; precision chip and tick; surface-aware footsteps and a landing cue forwarded to AudioFeedback. - Gamepad support (sticks, deadzone, face buttons, bumpers), a control legend that follows the active device, and named actions for the remaining keys. - Key/button remapping in Settings, persisted as input_bindings and applied by InputBindings (mouse and gamepad axes are never removed). - smoke_movement.gd (windowed): climbs the steps, checks precision speed, chip, footsteps and landing; new 11-precision-walk capture and catalogue entry. Verification: headless 208/208, Python tooling 56/56, all 11 smokes pass, compare 11 good / 0 bad, movement judge match. Version 0.5.0. Only optional Future ideas remain in TODO.
This commit is contained in:
1 parent
2a4e0d1998
commit
a27e8cb74c
35 files changed
+1608
-185
No files matched your search
@@ -0,0 +1,67 @@
|
||||
class_name InputBindings
|
||||
extends RefCounted
|
||||
## Key/button remaps persisted through the settings panel. Games keep working
|
||||
## with the defaults in `project.godot`; only actions the player rebinds are
|
||||
## rewritten, and remaps never remove mouse or gamepad axis events.
|
||||
|
||||
const REMAPPABLE := [
|
||||
"move_forward", "move_back", "move_left", "move_right",
|
||||
"walk_slow", "inspect", "wand", "radio_emergency", "action_menu",
|
||||
]
|
||||
|
||||
## Turn an input event into a storable descriptor, or {} when it is not a key or
|
||||
## pad button (mouse buttons and axes are deliberately not remappable here).
|
||||
static func descriptor_from_event(event: InputEvent) -> Dictionary:
|
||||
if event is InputEventKey:
|
||||
var key := event as InputEventKey
|
||||
var code := key.physical_keycode if key.physical_keycode != 0 else key.keycode
|
||||
if code == 0:
|
||||
return {}
|
||||
return {"type": "key", "code": int(code)}
|
||||
if event is InputEventJoypadButton:
|
||||
return {"type": "joy", "button": int((event as InputEventJoypadButton).button_index)}
|
||||
return {}
|
||||
|
||||
static func label_for(descriptor: Dictionary) -> String:
|
||||
match str(descriptor.get("type", "")):
|
||||
"key":
|
||||
return OS.get_keycode_string(int(descriptor.get("code", 0)))
|
||||
"joy":
|
||||
return "Pad %d" % int(descriptor.get("button", 0))
|
||||
_:
|
||||
return ""
|
||||
|
||||
## First key/button currently bound to the action, used to label the UI.
|
||||
static func current_descriptor(action: String) -> Dictionary:
|
||||
if not InputMap.has_action(action):
|
||||
return {}
|
||||
for event in InputMap.action_get_events(action):
|
||||
var descriptor := descriptor_from_event(event)
|
||||
if not descriptor.is_empty():
|
||||
return descriptor
|
||||
return {}
|
||||
|
||||
## Rewrite the actions present in `bindings`. Existing key/button events for
|
||||
## those actions are replaced; mouse and joypad-axis events are left alone.
|
||||
static func apply(bindings: Dictionary) -> void:
|
||||
for action in bindings.keys():
|
||||
if not InputMap.has_action(action):
|
||||
continue
|
||||
var descriptor: Dictionary = bindings[action]
|
||||
if descriptor.is_empty():
|
||||
continue
|
||||
for event in InputMap.action_get_events(action):
|
||||
if event is InputEventKey or event is InputEventJoypadButton:
|
||||
InputMap.action_erase_event(action, event)
|
||||
InputMap.action_add_event(action, event_for(descriptor))
|
||||
|
||||
static func event_for(descriptor: Dictionary) -> InputEvent:
|
||||
match str(descriptor.get("type", "")):
|
||||
"joy":
|
||||
var button := InputEventJoypadButton.new()
|
||||
button.button_index = int(descriptor.get("button", 0))
|
||||
return button
|
||||
_:
|
||||
var key := InputEventKey.new()
|
||||
key.physical_keycode = int(descriptor.get("code", 0))
|
||||
return key
|
||||
Reference in new issue
Block a user