nmarshall commited on
Commit
ae1c46e
·
1 Parent(s): 4f7acc4

qvm: grounded gait and footsteps

Browse files
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()