qvm: weapon lowering and raising
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_WEAPON_PHASES.md +11 -0
- docs/research/README.md +3 -0
- docs/research/qvm-weapon-phases.json +0 -0
- scripts/analyze_qvm_weapon_phases.py +30 -0
- scripts/test_qvm_weapon_phases.py +33 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -424,3 +424,6 @@ Research progress: RE-577 [report](research/QVM_WEAPON_READY.md); remaining nati
|
|
| 424 |
|
| 425 |
|
| 426 |
Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
|
| 425 |
|
| 426 |
Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
Research progress: RE-579 [report](research/QVM_WEAPON_PHASES.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -782,3 +782,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 782 |
|
| 783 |
|
| 784 |
**RE-578 — Reload completion packed ammo transfer and re-entry:** 51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). [Evidence](research/QVM_RELOAD_COMPLETE.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 782 |
|
| 783 |
|
| 784 |
**RE-578 — Reload completion packed ammo transfer and re-entry:** 51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). [Evidence](research/QVM_RELOAD_COMPLETE.md).
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
**RE-579 — Weapon lowering raising and frame gates:** 51633..51801 completes state2 via49028, resets state1 to0 and selects a separate class-based torso table, then gates other states on FRAME232 and FRAME36 before firing entry51802. [Evidence](research/QVM_WEAPON_PHASES.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -738,3 +738,6 @@ Research progress: RE-577 [report](research/QVM_WEAPON_READY.md); remaining nati
|
|
| 738 |
|
| 739 |
|
| 740 |
Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
|
| 739 |
|
| 740 |
Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
Research progress: RE-579 [report](research/QVM_WEAPON_PHASES.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -586,3 +586,8 @@ Actual54444 reload/interaction order exposes timer interference before the weapo
|
|
| 586 |
## 2026-10-20 — RE-578 Reload completion packed ammo transfer and re-entry (separate task commit)
|
| 587 |
|
| 588 |
51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). 115 independent probes check increment/replacement, full/partial magazines, reserve0/1/2 wrap, preserved ID/mode/unselected words and live reload re-entry gates. Full suite: 831 tests pass. Report regenerates byte-identically (878739 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
## 2026-10-20 — RE-578 Reload completion packed ammo transfer and re-entry (separate task commit)
|
| 587 |
|
| 588 |
51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). 115 independent probes check increment/replacement, full/partial magazines, reserve0/1/2 wrap, preserved ID/mode/unselected words and live reload re-entry gates. Full suite: 831 tests pass. Report regenerates byte-identically (878739 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 589 |
+
|
| 590 |
+
|
| 591 |
+
## 2026-10-20 — RE-579 Weapon lowering raising and frame gates (separate task commit)
|
| 592 |
+
|
| 593 |
+
51633..51801 completes state2 via49028, resets state1 to0 and selects a separate class-based torso table, then gates other states on FRAME232 and FRAME36 before firing entry51802. 87 fixtures independently check completion fallback and state handoff, all classes−1..8, button priority, animation timer/type guards and both frame gates. Full suite: 835 tests pass. Report regenerates byte-identically (796730 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
|
@@ -778,3 +778,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 778 |
|
| 779 |
|
| 780 |
- [x] RE-578 Reload completion packed ammo transfer and re-entry ([report](research/QVM_RELOAD_COMPLETE.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
|
| 779 |
|
| 780 |
- [x] RE-578 Reload completion packed ammo transfer and re-entry ([report](research/QVM_RELOAD_COMPLETE.md)).
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
- [x] RE-579 Weapon lowering raising and frame gates ([report](research/QVM_WEAPON_PHASES.md)).
|
docs/research/QVM_WEAPON_PHASES.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Weapon lowering raising and frame gates
|
| 2 |
+
|
| 3 |
+
RE-579. Reproduce with `python3 scripts/analyze_qvm_weapon_phases.py`; check with `python3 scripts/test_qvm_weapon_phases.py`. The [machine report](qvm-weapon-phases.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
State2 calls actual49028 and exits. State1 resets PS148 to0, then button8192/16384 requests torso50; otherwise initialized jump table21284..21312 maps classes0/1/5/7 to67,2 to66,3 to78,4 to72 and6 to65; out-of-range defaults67. Unlike49117's idle torso selector, this table does not inspect tag22/25. Actual39482 applies timer/type/toggle guards; phase completion itself still sets state0 when animation is blocked.
|
| 6 |
+
|
| 7 |
+
Other weapon states reach a pair of frame-word gates: nonzero FRAME232 or FRAME36 exits, otherwise51802 is reached. These are movement-frame words, distinct from same-offset player-state fields; their gameplay interpretation remains open.87 fixtures include actual49028 child effects and numeric command/fallback lookup.
|
| 8 |
+
|
| 9 |
+
Standalone phase probes intentionally do not run prior positive-timer guards. Full parent composition must preserve those gates and existing parent locals before reaching this slice. Remaining: compose recovered pre-firing parent paths, resume main suffix only on proven returns, and recover mode-specific51802+ firing/availability logic.
|
| 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
|
@@ -339,3 +339,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 339 |
|
| 340 |
|
| 341 |
[RE-578: Reload completion packed ammo transfer and re-entry](QVM_RELOAD_COMPLETE.md). Reproduce with `python3 scripts/analyze_qvm_reload_complete.py`; check with `python3 scripts/test_qvm_reload_complete.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
|
| 341 |
[RE-578: Reload completion packed ammo transfer and re-entry](QVM_RELOAD_COMPLETE.md). Reproduce with `python3 scripts/analyze_qvm_reload_complete.py`; check with `python3 scripts/test_qvm_reload_complete.py`.
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
[RE-579: Weapon lowering raising and frame gates](QVM_WEAPON_PHASES.md). Reproduce with `python3 scripts/analyze_qvm_weapon_phases.py`; check with `python3 scripts/test_qvm_weapon_phases.py`.
|
docs/research/qvm-weapon-phases.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_weapon_phases.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""51633..51801 lowering/raising completion and two frame-field gates."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source,execute
|
| 6 |
+
from qvm_weapon_support import weapon_seed,weapon_state
|
| 7 |
+
from qvm_suffix_support import handlers
|
| 8 |
+
from analyze_qvm_weapon_prefix import LOCAL
|
| 9 |
+
from analyze_qvm_weapon_complete import complete
|
| 10 |
+
from analyze_qvm_acceleration import PS,FRAME
|
| 11 |
+
from analyze_qvm import write_json
|
| 12 |
+
|
| 13 |
+
def phases(ins,m,cmd=(0,0,0)):
|
| 14 |
+
m.setdefault(FRAME+36,0);before=weapon_state(m);klass=m[4032+432*(before['packed']&255)];f36=m[FRAME+36];hs=handlers()
|
| 15 |
+
def completion(i,m,a,c):m,r=complete(i,m,c);return m,c,0,r
|
| 16 |
+
hs[49028]=completion;m,c,r=execute(ins,51633,53046,m,cmd,LOCAL,hs,boundaries=[51802])
|
| 17 |
+
return m,{'initial':before,'buttons':m[FRAME+20],'kind':m[PS+4],'class':klass,'frame36':f36,'execution':r,'final':weapon_state(m)}
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
q,raw,h,ins=source();rows=[]
|
| 21 |
+
for desired,timer,kind in itertools.product([1,22,255],[0,-1,100],[0,3]):
|
| 22 |
+
m=weapon_seed(raw,h,desired=desired);m.update({PS+312:22,PS+316:1,PS+148:2,PS+44:timer,PS+4:kind,FRAME+20:0});rows.append(phases(ins,m)[1])
|
| 23 |
+
for klass,buttons,tt in itertools.product(range(-1,9),[0,8192,16384],[0,1]):
|
| 24 |
+
m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:1,4032+432*22:klass,FRAME+20:buttons,PS+80:tt});rows.append(phases(ins,m)[1])
|
| 25 |
+
for ws,a,b in itertools.product([0,3],[0,1],[0,1]):
|
| 26 |
+
m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:ws,FRAME+232:a,FRAME+36:b});rows.append(phases(ins,m)[1])
|
| 27 |
+
m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:1,PS+4:3});rows.append(phases(ins,m)[1])
|
| 28 |
+
table={str(a):weapon_seed(raw,h)[a] for a in range(21284,21316,4)}
|
| 29 |
+
write_json(Path('docs/research/qvm-weapon-phases.json'),{'source':q['source'],'sha256':q['sha256'],'jump_table':table,'probes':rows,'limits':'Finite51633..51801, actual49028/39482 children and initialized class table. Standalone entry may violate earlier timer gates; type/phase/frame legality remains open. Stops51802 before mode-specific firing.'})
|
| 30 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_weapon_phases.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest,copy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_weapon_complete import completion_state
|
| 4 |
+
from test_qvm_torso_selector import signed
|
| 5 |
+
|
| 6 |
+
def phase_request(p):
|
| 7 |
+
if p['initial']['weapon_state']!=1:return None
|
| 8 |
+
if p['buttons']&(8192|16384):return 50
|
| 9 |
+
return {0:67,1:67,2:66,3:78,4:72,5:67,6:65,7:67}.get(p['class'],67)
|
| 10 |
+
|
| 11 |
+
def phase_state(p):
|
| 12 |
+
a=copy.deepcopy(p['initial']);stop=53046
|
| 13 |
+
if a['weapon_state']==2:return completion_state(p['execution']['children'][0]['effect']),stop
|
| 14 |
+
if a['weapon_state']==1:
|
| 15 |
+
a['weapon_state']=0;r=phase_request(p)
|
| 16 |
+
if p['kind']<3 and signed(a['torso_timer'])<=0 and a['torso']!=r and a['torso']&~128!=r:a['torso']=((a['torso']&128)^128)|r
|
| 17 |
+
elif not a['frame232'] and not p['frame36']:stop=51802
|
| 18 |
+
return a,stop
|
| 19 |
+
class WeaponPhasesTests(unittest.TestCase):
|
| 20 |
+
@classmethod
|
| 21 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-phases.json').read_text())
|
| 22 |
+
def test_complete_exact_phase_and_frame_state(self):
|
| 23 |
+
self.assertEqual(len(self.r['probes']),87)
|
| 24 |
+
for p in self.r['probes']:a,stop=phase_state(p);self.assertEqual(p['final'],a);self.assertEqual(p['execution']['stop'],stop)
|
| 25 |
+
def test_source_jump_table_and_no_tag_exception(self):self.assertEqual(list(self.r['jump_table'].values()),[51733,51740,51747,51754,51761,51733,51768,51775]);self.assertTrue(any(p['class']==4 and phase_request(p)==72 for p in self.r['probes']))
|
| 26 |
+
def test_actual_child_dispatch_and_request(self):
|
| 27 |
+
for p in self.r['probes']:
|
| 28 |
+
ws=p['initial']['weapon_state'];self.assertEqual([c['target'] for c in p['execution']['calls']],[49028] if ws==2 else [39482] if ws==1 else [])
|
| 29 |
+
if ws==1:self.assertEqual(p['execution']['calls'][0]['args'],{'8':phase_request(p)})
|
| 30 |
+
def test_raising_sets_idle_even_if_animation_blocked(self):
|
| 31 |
+
rows=[p for p in self.r['probes'] if p['initial']['weapon_state']==1 and (p['kind']==3 or p['initial']['torso_timer']>0)];self.assertTrue(rows)
|
| 32 |
+
for p in rows:self.assertEqual(p['final']['weapon_state'],0);self.assertEqual(p['final']['torso'],p['initial']['torso'])
|
| 33 |
+
if __name__=='__main__':unittest.main()
|