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

qvm: airborne locomotion

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -391,3 +391,6 @@ Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining n
391
 
392
 
393
  Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
391
 
392
 
393
  Research progress: RE-566 [report](research/QVM_WEAPON_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.
docs/FINDINGS.md CHANGED
@@ -749,3 +749,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
749
 
750
 
751
  **RE-566 — Mode switch and ordered weapon prefix:** 49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. [Evidence](research/QVM_WEAPON_PREFIX.md).
 
 
 
 
749
 
750
 
751
  **RE-566 — Mode switch and ordered weapon prefix:** 49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. [Evidence](research/QVM_WEAPON_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).
docs/PROVISIONAL_TODO.md CHANGED
@@ -703,3 +703,6 @@ Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining n
703
 
704
 
705
  Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
703
 
704
 
705
  Research progress: RE-566 [report](research/QVM_WEAPON_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.
docs/RESEARCH_LEDGER.md CHANGED
@@ -531,3 +531,8 @@ RE-549–558 each has its own code/tests/report/documentation commit. All759 tes
531
  ## 2026-10-20 — RE-566 Mode switch and ordered weapon prefix (separate task commit)
532
 
533
  49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. 82 mode fixtures and84 prefix fixtures independently check complete writes, source-order child state, death/flag early exits and negative timer preservation. Full suite: 787 tests pass. Report regenerates byte-identically (642934 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
531
  ## 2026-10-20 — RE-566 Mode switch and ordered weapon prefix (separate task commit)
532
 
533
  49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. 82 mode fixtures and84 prefix fixtures independently check complete writes, source-order child state, death/flag early exits and negative timer preservation. Full suite: 787 tests pass. Report regenerates byte-identically (642934 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
534
+
535
+
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.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -743,3 +743,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
743
 
744
 
745
  - [x] RE-566 Mode switch and ordered weapon prefix ([report](research/QVM_WEAPON_PREFIX.md)).
 
 
 
 
743
 
744
 
745
  - [x] RE-566 Mode switch and ordered weapon prefix ([report](research/QVM_WEAPON_PREFIX.md)).
746
+
747
+
748
+ - [x] RE-567 Airborne locomotion animation and bob cycle ([report](research/QVM_LOCOMOTION_PREFIX.md)).
docs/research/QVM_LOCOMOTION_PREFIX.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Airborne locomotion animation and bob cycle
2
+
3
+ RE-567. Reproduce with `python3 scripts/analyze_qvm_locomotion_prefix.py`; check with `python3 scripts/test_qvm_locomotion_prefix.py`. The [machine report](qvm-locomotion-prefix.json) retains source identity and bounded fixtures.
4
+
5
+ 47884 begins by loading initialized weapon class/tag and computing sqrt(vX²+vY²), storing the float at FRAME216. PS216 is a distinct retained word. Airborne is ground entity1023. Water level>1 first requests legs25, otherwise crouch flag1 requests42. Flags12 then select legs20 for nonzero signed forward,21 for zero, with the same torso request when flag8 is set. Absolute forward>100 advances bob by float32(.4×integer dt), truncates and masks255; crossing ((old+64) xor(new+64)) bit128 emits5.
6
+
7
+ If flags12 are absent, state1024 requests legs0 then torso0; otherwise flag2 requests torso68 then legs42. Recovered conditional animation helpers preserve timer/type/toggle rules, so earlier requests in the same body affect later ones.120 probes include masked animation aliases, positive timers, kind3, bob255 wrap, signed−128 input and high vertical velocity excluded from speed.
8
+
9
+ The entire airborne path runs to48714. Grounded execution explicitly stops at48209 before its animation/gait branch; that continuation is the next task. Native sqrt is supplied for finite values, and no actual skeletal playback, collision or original runtime executes. Legal animation naming and live speed/state reference parity remain 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
@@ -304,3 +304,6 @@ The extension collector adds five focused tests for native tail calls, command-r
304
 
305
 
306
  [RE-566: Mode switch and ordered weapon prefix](QVM_WEAPON_PREFIX.md). Reproduce with `python3 scripts/analyze_qvm_weapon_prefix.py`; check with `python3 scripts/test_qvm_weapon_prefix.py`.
 
 
 
 
304
 
305
 
306
  [RE-566: Mode switch and ordered weapon prefix](QVM_WEAPON_PREFIX.md). Reproduce with `python3 scripts/analyze_qvm_weapon_prefix.py`; check with `python3 scripts/test_qvm_weapon_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`.
docs/research/qvm-locomotion-prefix.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_locomotion_prefix.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """47884 horizontal-speed cache and complete airborne animation/bob branch."""
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,weapon_words
7
+ from analyze_qvm_acceleration import PS,FRAME,word
8
+ from analyze_qvm_slide_prefix import vec
9
+ from analyze_qvm import write_json
10
+ LOCAL=0x71000000
11
+
12
+ def locomotion_prefix(ins,m,cmd=(0,0,0)):
13
+ before=state(m);velocity=vec(m,PS+32);ps216=m[PS+216];cfg=weapon_words(m,before['packed']&255)
14
+ m,c,r=execute(ins,47885,48714,m,cmd,LOCAL,handlers(),boundaries=[48209])
15
+ return m,{'initial':before,'commands':list(cmd),'buttons':m[FRAME+20],'level':m[FRAME+212],'dt':m[35023932],'kind':m[PS+4],'velocity':velocity,'config':cfg,'ps216_before':ps216,'ps216_after':m[PS+216],'execution':r,'final':state(m)}
16
+
17
+ def main():
18
+ q,raw,h,ins=source();rows=[]
19
+ for flags,level,forward,special in itertools.product([0,1,2,4,8,9,12],[0,2],[0,100,101,-128],[0,1024]):
20
+ cmd=(forward,0,0);m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:1,PS+12:flags,PS+220:special,PS+68:1023,FRAME+212:level,PS+8:60,PS+32:word(3),PS+36:word(4),PS+40:word(-999),35023932:16});rows.append(locomotion_prefix(ins,m,cmd)[1])
21
+ for changes in [{PS+68:8},{PS+4:3},{PS+72:1},{PS+80:1},{PS+8:255},{35023932:200},{PS+76:148},{PS+84:148}]:
22
+ cmd=(127,0,0);m=weapon_config(seed(raw,h,cmd),raw,h);m.update({PS+312:22,PS+12:8,PS+68:1023,PS+8:60,35023932:16});m.update(changes);rows.append(locomotion_prefix(ins,m,cmd)[1])
23
+ write_json(Path('docs/research/qvm-locomotion-prefix.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'47884 prefix and complete airborne path only; grounded branch starts48209 and is stopped. Actual animation/event children, supplied sqrt, finite state; no skeletal rendering or physics executes.'})
24
+ if __name__=='__main__':main()
scripts/test_qvm_locomotion_prefix.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy,math
2
+ from pathlib import Path
3
+ from test_qvm_ground import word,n,f
4
+ from test_qvm_torso_selector import signed
5
+ from test_qvm_water_events import enqueue
6
+
7
+ def animation(a,entry,request,kind):
8
+ field,timer=('legs','leg_timer') if entry==39439 else ('torso','torso_timer')
9
+ if kind<3 and signed(a[timer])<=0 and a[field]!=request and a[field]&~128!=request:a[field]=((a[field]&128)^128)|request
10
+
11
+ def bob(a,dt,rate):
12
+ old=signed(a['bob']);new=int(f(f(old)+f(rate*f(dt))))&255;a['bob']=new;return bool(((old+64)^(new+64))&128)
13
+
14
+ def prefix_state(p):
15
+ a=copy.deepcopy(p['initial']);v=list(map(n,p['velocity']));a['speed']=word(math.sqrt(f(f(v[0]*v[0])+f(v[1]*v[1]))));requests=[]
16
+ def anim(entry,arg):requests.append((entry,arg));animation(a,entry,arg,p['kind'])
17
+ if a['ground']!=1023:return a,requests
18
+ flags=a['flags'];forward=p['commands'][0]
19
+ if p['level']>1:anim(39439,25)
20
+ elif flags&1:anim(39439,42)
21
+ if flags&12:
22
+ arg=20 if forward else 21;anim(39439,arg)
23
+ if flags&8:anim(39482,arg)
24
+ if abs(forward)>100 and bob(a,p['dt'],n(1053609165)):requests.append((39238,5));a['queue']=enqueue(a['queue'],[5])
25
+ elif a['state220']&1024:anim(39439,0);anim(39482,0)
26
+ elif flags&2:anim(39482,68);anim(39439,42)
27
+ return a,requests
28
+ class LocomotionPrefixTests(unittest.TestCase):
29
+ @classmethod
30
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-locomotion-prefix.json').read_text())
31
+ def test_airborne_complete_state_and_priority(self):
32
+ self.assertEqual(len(self.r['probes']),120)
33
+ for p in self.r['probes']:self.assertEqual(p['final'],prefix_state(p)[0])
34
+ def test_actual_ordered_animation_event_children(self):
35
+ for p in self.r['probes']:self.assertEqual([(c['target'],c['args']['8']) for c in p['execution']['calls'] if c['target']!=-107],prefix_state(p)[1])
36
+ def test_horizontal_speed_frame_cache_not_ps216(self):
37
+ for p in self.r['probes']:
38
+ self.assertEqual(p['ps216_before'],p['ps216_after']);self.assertEqual(p['execution']['calls'][0]['target'],-107);v=list(map(n,p['velocity']));self.assertEqual(p['execution']['calls'][0]['args']['8'],word(f(f(v[0]*v[0])+f(v[1]*v[1]))))
39
+ def test_grounded_boundary_and_strict_command_threshold(self):
40
+ for p in self.r['probes']:self.assertEqual(p['execution']['stop'],48209 if p['initial']['ground']!=1023 else 48714)
41
+ self.assertTrue(any(p['commands'][0]==100 and p['final']['bob']==p['initial']['bob'] for p in self.r['probes']));self.assertTrue(any(p['commands'][0]==101 and p['final']['bob']!=p['initial']['bob'] for p in self.r['probes']))
42
+ if __name__=='__main__':unittest.main()