qvm: grounded gait and footsteps
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/QVM_LOCOMOTION.md +11 -0
- docs/research/README.md +3 -0
- docs/research/qvm-locomotion.json +0 -0
- scripts/analyze_qvm_locomotion.py +29 -0
- scripts/test_qvm_locomotion.py +52 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -394,3 +394,6 @@ Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining nat
|
|
| 394 |
|
| 395 |
|
| 396 |
Research progress: RE-567 [report](research/QVM_LOCOMOTION_PREFIX.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
|
| 395 |
|
| 396 |
Research progress: RE-567 [report](research/QVM_LOCOMOTION_PREFIX.md); remaining native/content/runtime/platform scope stays open.
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
Research progress: RE-568 [report](research/QVM_LOCOMOTION.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -752,3 +752,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 752 |
|
| 753 |
|
| 754 |
**RE-567 — Airborne locomotion animation and bob cycle:** 47884 caches horizontal speed in FRAME216, then airborne animation priority combines water/crouch, traversal flags and state1024; traversal bob advances only for absolute forward input strictly above100. [Evidence](research/QVM_LOCOMOTION_PREFIX.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
|
| 753 |
|
| 754 |
**RE-567 — Airborne locomotion animation and bob cycle:** 47884 caches horizontal speed in FRAME216, then airborne animation priority combines water/crouch, traversal flags and state1024; traversal bob advances only for absolute forward input strictly above100. [Evidence](research/QVM_LOCOMOTION_PREFIX.md).
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
**RE-568 — Complete locomotion gait bob and footstep consumer:** Complete47884 selects grounded stance/gait requests, advances a masked bob cycle at.3 or.4 rates, and emits surface39, shallow32 or water41 events only on cycle crossings with route-specific suppression. [Evidence](research/QVM_LOCOMOTION.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -706,3 +706,6 @@ Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining nat
|
|
| 706 |
|
| 707 |
|
| 708 |
Research progress: RE-567 [report](research/QVM_LOCOMOTION_PREFIX.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 706 |
|
| 707 |
|
| 708 |
Research progress: RE-567 [report](research/QVM_LOCOMOTION_PREFIX.md); remaining native/content/runtime/platform scope stays open.
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
Research progress: RE-568 [report](research/QVM_LOCOMOTION.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -536,3 +536,8 @@ RE-549–558 each has its own code/tests/report/documentation commit. All759 tes
|
|
| 536 |
## 2026-10-20 — RE-567 Airborne locomotion animation and bob cycle (separate task commit)
|
| 537 |
|
| 538 |
47884 caches horizontal speed in FRAME216, then airborne animation priority combines water/crouch, traversal flags and state1024; traversal bob advances only for absolute forward input strictly above100. 120 probes independently check horizontal sqrt input, frame versus PS field identity, all request order/effects, bob wrap, event5 threshold crossing and grounded stopping boundary. Full suite: 791 tests pass. Report regenerates byte-identically (1446102 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
## 2026-10-20 — RE-567 Airborne locomotion animation and bob cycle (separate task commit)
|
| 537 |
|
| 538 |
47884 caches horizontal speed in FRAME216, then airborne animation priority combines water/crouch, traversal flags and state1024; traversal bob advances only for absolute forward input strictly above100. 120 probes independently check horizontal sqrt input, frame versus PS field identity, all request order/effects, bob wrap, event5 threshold crossing and grounded stopping boundary. Full suite: 791 tests pass. Report regenerates byte-identically (1446102 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
## 2026-10-20 — RE-568 Complete locomotion gait bob and footstep consumer (separate task commit)
|
| 542 |
+
|
| 543 |
+
Complete47884 selects grounded stance/gait requests, advances a masked bob cycle at.3 or.4 rates, and emits surface39, shallow32 or water41 events only on cycle crossings with route-specific suppression. 120 complete fixtures independently check idle speed5 boundary, crouch/walk/run/backward/boost/special routes, class/tag torso choices, exact float bob arithmetic and surface/water event effects. Full suite: 795 tests pass. Report regenerates byte-identically (1570164 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -746,3 +746,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 746 |
|
| 747 |
|
| 748 |
- [x] RE-567 Airborne locomotion animation and bob cycle ([report](research/QVM_LOCOMOTION_PREFIX.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
|
| 747 |
|
| 748 |
- [x] RE-567 Airborne locomotion animation and bob cycle ([report](research/QVM_LOCOMOTION_PREFIX.md)).
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
- [x] RE-568 Complete locomotion gait bob and footstep consumer ([report](research/QVM_LOCOMOTION.md)).
|
docs/research/QVM_LOCOMOTION.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Complete locomotion gait bob and footstep consumer
|
| 2 |
+
|
| 3 |
+
RE-568. Reproduce with `python3 scripts/analyze_qvm_locomotion.py`; check with `python3 scripts/test_qvm_locomotion.py`. The [machine report](qvm-locomotion.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
The grounded continuation48209..48713 now runs after the actual47884 prefix in the same parent frame. No forward/right input with speed>=5 retains the bob/animation state; below5 resets bob to0 and selects idle41 or crouch torso68/legs42. Moving crouch chooses torso69/76 or68/71 from entity flag1 and class4/tag22, plus legs39/40/42. Button16 selects walking torso70 and legs22/23 (34/35 with state128). Running selects torso63 for classes0..3,74 for4,62 otherwise; backwards flag16384 selects24 (35 special), boost button256 with right0 and forward>80 selects43 (34 special), otherwise38 (34 special).
|
| 6 |
+
|
| 7 |
+
Crouch/walk use source float.3, run uses.4. Bob advances with float32 integer-dt multiplication/addition, truncates and masks255. On ((old+64) xor(new+64)) bit128 crossing, dry running calls45887: surface8192 suppresses event39, otherwise its parameter is surface>>20. Level1 emits32, level2 emits41, level3 is silent; dry walk/crouch suppress footsteps. Actual animation/event children run with their timer/type/toggle gates.
|
| 8 |
+
|
| 9 |
+
120 complete fixtures include idle speed exactly5, signed commands, boost80/81/strafe boundaries, surface parameters and suppression, bob wrap, water levels0..3 and an airborne regression. Remaining: map requests/events to skeletal/audio assets, validate legal live state and implement/source-compare the Rust animation/audio controller. Full native rendering/gameplay remains open.
|
| 10 |
+
|
| 11 |
+
No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
|
docs/research/README.md
CHANGED
|
@@ -307,3 +307,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 307 |
|
| 308 |
|
| 309 |
[RE-567: Airborne locomotion animation and bob cycle](QVM_LOCOMOTION_PREFIX.md). Reproduce with `python3 scripts/analyze_qvm_locomotion_prefix.py`; check with `python3 scripts/test_qvm_locomotion_prefix.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
|
| 308 |
|
| 309 |
[RE-567: Airborne locomotion animation and bob cycle](QVM_LOCOMOTION_PREFIX.md). Reproduce with `python3 scripts/analyze_qvm_locomotion_prefix.py`; check with `python3 scripts/test_qvm_locomotion_prefix.py`.
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
[RE-568: Complete locomotion gait bob and footstep consumer](QVM_LOCOMOTION.md). Reproduce with `python3 scripts/analyze_qvm_locomotion.py`; check with `python3 scripts/test_qvm_locomotion.py`.
|
docs/research/qvm-locomotion.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_locomotion.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Complete47884 including grounded stance/gaits/bob/surface-water events."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source,execute
|
| 6 |
+
from qvm_suffix_support import seed,state,handlers,weapon_config
|
| 7 |
+
from analyze_qvm_locomotion_prefix import locomotion_prefix,LOCAL
|
| 8 |
+
from analyze_qvm_acceleration import PS,FRAME,word
|
| 9 |
+
from analyze_qvm import write_json
|
| 10 |
+
|
| 11 |
+
def locomotion(ins,m,cmd=(0,0,0)):
|
| 12 |
+
surface=m[35023988];m,p=locomotion_prefix(ins,m,cmd);prefix=p.pop('execution');after=state(m);tail=None
|
| 13 |
+
if prefix['stop']==48209:m,c,tail=execute(ins,48209,48714,m,cmd,LOCAL,handlers())
|
| 14 |
+
return m,{**p,'surface':surface,'prefix_execution':prefix,'after_prefix':after,'ground_execution':tail,'final':state(m)}
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
q,raw,h,ins=source();rows=[]
|
| 18 |
+
for flags,buttons,special,level in itertools.product([0,1,16384,16385],[0,16,256],[0,128],[0,1,2,3]):
|
| 19 |
+
cmd=(127,0,0);m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:22,PS+68:8,PS+12:flags,FRAME+20:buttons,PS+220:special,FRAME+212:level,PS+8:60,35023932:16});rows.append(locomotion(ins,m,cmd)[1])
|
| 20 |
+
for cmd,v,flags in itertools.product([(0,0,0),(0,1,0)],[(0,0,50),(3,4,50)],[0,1]):
|
| 21 |
+
m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:1,PS+68:8,PS+12:flags,PS+8:60})
|
| 22 |
+
for j,x in enumerate(v):m[PS+32+4*j]=word(x)
|
| 23 |
+
rows.append(locomotion(ins,m,cmd)[1])
|
| 24 |
+
for changes in [{PS+312:1},{PS+312:25},{PS+312:14},{PS+104:1,PS+12:1},{PS+312:25,PS+104:1,PS+12:1},{PS+12:1,PS+312:25},{PS+72:1,PS+80:1},{PS+4:3},{PS+8:255},{35023988:2<<20},{35023988:8192|(5<<20)},{PS+68:1023,PS+12:8}]:
|
| 25 |
+
cmd=(127,0,0);m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:22,PS+68:8,PS+8:60,FRAME+20:0});m.update(changes);rows.append(locomotion(ins,m,cmd)[1])
|
| 26 |
+
for cmd in [(80,0,0),(81,0,0),(81,1,0),(-128,0,0)]:
|
| 27 |
+
m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:22,PS+68:8,PS+8:60,FRAME+20:256});rows.append(locomotion(ins,m,cmd)[1])
|
| 28 |
+
write_json(Path('docs/research/qvm-locomotion.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Complete finite47884 with actual animation/event helpers and supplied horizontal sqrt. Initialized class/tag/jump words; legal gameplay state, gait naming and audible/skeletal native playback remain unverified.'})
|
| 29 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_locomotion.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest,copy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_ground import n
|
| 4 |
+
from test_qvm_torso_selector import signed
|
| 5 |
+
from test_qvm_water_events import enqueue
|
| 6 |
+
from test_qvm_locomotion_prefix import prefix_state,animation,bob
|
| 7 |
+
|
| 8 |
+
def locomotion_state(p):
|
| 9 |
+
a,requests=prefix_state(p)
|
| 10 |
+
if a['ground']==1023:return a,requests
|
| 11 |
+
fw,right,_=p['commands'];flags=a['flags'];klass=signed(p['config']['class']);tag=p['config']['tag'];special=bool(a['state220']&128)
|
| 12 |
+
def anim(entry,arg):requests.append((entry,arg));animation(a,entry,arg,p['kind'])
|
| 13 |
+
if fw==0 and right==0:
|
| 14 |
+
if n(a['speed'])>=5:return a,requests
|
| 15 |
+
a['bob']=0
|
| 16 |
+
if flags&1:anim(39482,68);anim(39439,42)
|
| 17 |
+
else:anim(39439,41)
|
| 18 |
+
return a,requests
|
| 19 |
+
sound=False
|
| 20 |
+
if flags&1:
|
| 21 |
+
rate=n(1050253722);stance=bool(a['entity_flags']&1)
|
| 22 |
+
anim(39482,(71 if stance else 76) if klass==4 and tag!=22 else (68 if stance else 69));anim(39439,42 if stance else 40 if flags&16384 else 39)
|
| 23 |
+
elif p['buttons']&16:
|
| 24 |
+
rate=n(1050253722);anim(39482,70);anim(39439,(35 if special else 23) if flags&16384 else (34 if special else 22))
|
| 25 |
+
else:
|
| 26 |
+
rate=n(1053609165);sound=True;anim(39482,63 if 0<=klass<=3 else 74 if klass==4 else 62)
|
| 27 |
+
anim(39439,(35 if special else 24) if flags&16384 else (34 if special else 43) if p['buttons']&256 and right==0 and fw>80 else (34 if special else 38))
|
| 28 |
+
if bob(a,p['dt'],rate):
|
| 29 |
+
level=p['level']
|
| 30 |
+
if level==0 and sound:
|
| 31 |
+
requests.append((45887,None))
|
| 32 |
+
if not p['surface']&8192:
|
| 33 |
+
slot=a['queue']['sequence']&1;a['queue']=enqueue(a['queue'],[39]);a['queue']['parameters'][slot]=p['surface']>>20
|
| 34 |
+
elif level in [1,2]:requests.append((39238,32 if level==1 else 41));a['queue']=enqueue(a['queue'],[32 if level==1 else 41])
|
| 35 |
+
return a,requests
|
| 36 |
+
class LocomotionTests(unittest.TestCase):
|
| 37 |
+
@classmethod
|
| 38 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-locomotion.json').read_text())
|
| 39 |
+
def test_full_stance_gait_and_bob_state(self):
|
| 40 |
+
self.assertEqual(len(self.r['probes']),120)
|
| 41 |
+
for p in self.r['probes']:self.assertEqual(p['final'],locomotion_state(p)[0])
|
| 42 |
+
def test_actual_request_order_and_surface_parameters(self):
|
| 43 |
+
for p in self.r['probes']:
|
| 44 |
+
calls=p['prefix_execution']['calls']+(p['ground_execution']['calls'] if p['ground_execution'] else []);self.assertEqual([(c['target'],None if c['target']==45887 else c['args']['8']) for c in calls if c['target']!=-107],locomotion_state(p)[1])
|
| 45 |
+
def test_speed5_is_retained_bob_below5_resets(self):
|
| 46 |
+
for p in self.r['probes']:
|
| 47 |
+
if p['initial']['ground']!=1023 and p['commands'][:2]==[0,0]:self.assertEqual(p['final']['bob'],0 if n(p['final']['speed'])<5 else p['initial']['bob'])
|
| 48 |
+
def test_crossing_water_and_suppressed_footsteps(self):
|
| 49 |
+
rows=self.r['probes'];self.assertTrue(any(p['surface']&8192 and p['final']['queue']==p['initial']['queue'] for p in rows));self.assertTrue(any(p['surface']==2<<20 and 2 in p['final']['queue']['parameters'] for p in rows))
|
| 50 |
+
for p in rows:
|
| 51 |
+
if p['level']==3:self.assertEqual(p['final']['queue'],p['initial']['queue'])
|
| 52 |
+
if __name__=='__main__':unittest.main()
|