qvm: weapon selection request
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_SELECT.md +11 -0
- docs/research/README.md +3 -0
- docs/research/qvm-weapon-select.json +0 -0
- scripts/analyze_qvm_weapon_select.py +30 -0
- scripts/test_qvm_weapon_select.py +31 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -403,3 +403,6 @@ Research progress: RE-569 [report](research/QVM_MAIN_CONSUMERS.md); remaining na
|
|
| 403 |
|
| 404 |
|
| 405 |
Research progress: RE-571 [report](research/QVM_WEAPON_LOOKUP.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
|
| 404 |
|
| 405 |
Research progress: RE-571 [report](research/QVM_WEAPON_LOOKUP.md); remaining native/content/runtime/platform scope stays open.
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
Research progress: RE-572 [report](research/QVM_WEAPON_SELECT.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -761,3 +761,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 761 |
|
| 762 |
|
| 763 |
**RE-571 — Weapon slot lookup and numeric return contract:** 31691 searches the first16 packed weapon slots, compares their low bytes to the full requested integer and returns the first index or−1 without changing game state. [Evidence](research/QVM_WEAPON_LOOKUP.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
|
| 762 |
|
| 763 |
**RE-571 — Weapon slot lookup and numeric return contract:** 31691 searches the first16 packed weapon slots, compares their low bytes to the full requested integer and returns the first index or−1 without changing game state. [Evidence](research/QVM_WEAPON_LOOKUP.md).
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
**RE-572 — Weapon selection request gates and transition effects:** 48798 validates requested IDs1..27 and actual slot presence, emits30/parameter0, sets state2/timer0-or300 and timed torso54, then resets counters/resources and clears bits64/8/512 without switching slots. [Evidence](research/QVM_WEAPON_SELECT.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -717,3 +717,6 @@ Research progress: RE-569 [report](research/QVM_MAIN_CONSUMERS.md); remaining na
|
|
| 717 |
|
| 718 |
|
| 719 |
Research progress: RE-571 [report](research/QVM_WEAPON_LOOKUP.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
|
| 718 |
|
| 719 |
Research progress: RE-571 [report](research/QVM_WEAPON_LOOKUP.md); remaining native/content/runtime/platform scope stays open.
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
Research progress: RE-572 [report](research/QVM_WEAPON_SELECT.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -551,3 +551,8 @@ Actual54444 reload/interaction order exposes timer interference before the weapo
|
|
| 551 |
## 2026-10-20 — RE-571 Weapon slot lookup and numeric return contract (separate task commit)
|
| 552 |
|
| 553 |
31691 searches the first16 packed weapon slots, compares their low bytes to the full requested integer and returns the first index or−1 without changing game state. 111 fixtures cover slot0/1/7/15, excluded slot16, duplicates, absent entries, upper-byte canaries and requests outside the low-byte range. Full suite: 803 tests pass. Report regenerates byte-identically (318642 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
## 2026-10-20 — RE-571 Weapon slot lookup and numeric return contract (separate task commit)
|
| 552 |
|
| 553 |
31691 searches the first16 packed weapon slots, compares their low bytes to the full requested integer and returns the first index or−1 without changing game state. 111 fixtures cover slot0/1/7/15, excluded slot16, duplicates, absent entries, upper-byte canaries and requests outside the low-byte range. Full suite: 803 tests pass. Report regenerates byte-identically (318642 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
## 2026-10-20 — RE-572 Weapon selection request gates and transition effects (separate task commit)
|
| 557 |
+
|
| 558 |
+
48798 validates requested IDs1..27 and actual slot presence, emits30/parameter0, sets state2/timer0-or300 and timed torso54, then resets counters/resources and clears bits64/8/512 without switching slots. 76 fixtures independently check every guard, slot15 versus excluded16, state3 magazine exception overridden by state8, actual child order and all retained weapon records. Full suite: 807 tests pass. Report regenerates byte-identically (531673 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
|
@@ -757,3 +757,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 757 |
|
| 758 |
|
| 759 |
- [x] RE-571 Weapon slot lookup and numeric return contract ([report](research/QVM_WEAPON_LOOKUP.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 757 |
|
| 758 |
|
| 759 |
- [x] RE-571 Weapon slot lookup and numeric return contract ([report](research/QVM_WEAPON_LOOKUP.md)).
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
- [x] RE-572 Weapon selection request gates and transition effects ([report](research/QVM_WEAPON_SELECT.md)).
|
docs/research/QVM_WEAPON_SELECT.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Weapon selection request gates and transition effects
|
| 2 |
+
|
| 3 |
+
RE-572. Reproduce with `python3 scripts/analyze_qvm_weapon_select.py`; check with `python3 scripts/test_qvm_weapon_select.py`. The [machine report](qvm-weapon-select.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
Complete48798 runs through49025 with actual31691 slot search,29913 event queue and39552 timed torso. Before range/presence checks, state3 with a nonempty magazine blocks unless PS220 bit8 is set. Requested integer must be1..27; weapon state2 and state bit1 reject. A missing first16-slot match rejects.
|
| 6 |
+
|
| 7 |
+
Acceptance emits event30 parameter0, sets PS148=2, sets PS44=0 if the currently selected low-byte ID is0 else300, requests timed torso54, zeros PS204/188/196 and clears state bits64/8/512. Selection itself leaves active slot and every packed weapon record unchanged. Animation durations are supplied321,0 or−1 records; kind3 blocks torso mutation but not duration storage.
|
| 8 |
+
|
| 9 |
+
76 probes distinguish request validation from the lookup's wider equality contract and preserve unrelated state. Completion49028 and its fallback behavior are the next step; live weapon legality and native animation/audio playback 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
|
@@ -318,3 +318,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 318 |
|
| 319 |
|
| 320 |
[RE-571: Weapon slot lookup and numeric return contract](QVM_WEAPON_LOOKUP.md). Reproduce with `python3 scripts/analyze_qvm_weapon_lookup.py`; check with `python3 scripts/test_qvm_weapon_lookup.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
|
| 320 |
[RE-571: Weapon slot lookup and numeric return contract](QVM_WEAPON_LOOKUP.md). Reproduce with `python3 scripts/analyze_qvm_weapon_lookup.py`; check with `python3 scripts/test_qvm_weapon_lookup.py`.
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
[RE-572: Weapon selection request gates and transition effects](QVM_WEAPON_SELECT.md). Reproduce with `python3 scripts/analyze_qvm_weapon_select.py`; check with `python3 scripts/test_qvm_weapon_select.py`.
|
docs/research/qvm-weapon-select.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_weapon_select.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Complete48798 weapon selection request with actual31691/animation/events."""
|
| 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_lookup import lookup
|
| 9 |
+
from analyze_qvm_acceleration import PS,FRAME
|
| 10 |
+
from analyze_qvm_animation_contacts import TABLE
|
| 11 |
+
from analyze_qvm import write_json
|
| 12 |
+
LOCAL=0x74000000
|
| 13 |
+
|
| 14 |
+
def select(ins,m,requested,cmd=(0,0,0)):
|
| 15 |
+
m=dict(m);before=weapon_state(m);m[LOCAL+56]=requested;duration=m[TABLE+m[FRAME+248]*2560+54*32+28];hs=handlers();hs[31691]=lookup
|
| 16 |
+
m,c,r=execute(ins,48799,49026,m,cmd,LOCAL,hs)
|
| 17 |
+
return m,{'requested':requested,'kind':m[PS+4],'duration':duration,'initial':before,'execution':r,'final':weapon_state(m)}
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
q,raw,h,ins=source();rows=[]
|
| 21 |
+
for request,where,old in itertools.product([-1,0,1,22,27,28],[-1,0,15,16],[0,1]):
|
| 22 |
+
m=weapon_seed(raw,h);m[PS+312]=old|(2<<8)
|
| 23 |
+
if where>=0:m[PS+312+4*where]=(request&255)|(3<<8)
|
| 24 |
+
rows.append(select(ins,m,request)[1])
|
| 25 |
+
for ws,mag,flags in itertools.product([0,2,3],[0,1],[0,8,1,512|64]):
|
| 26 |
+
m=weapon_seed(raw,h);m.update({PS+312:1|(mag<<8),PS+312+15*4:22,PS+148:ws,PS+220:flags});rows.append(select(ins,m,22)[1])
|
| 27 |
+
for changes in [{PS+4:3},{PS+80:999},{TABLE+54*32+28:0},{TABLE+54*32+28:-1}]:
|
| 28 |
+
m=weapon_seed(raw,h);m.update({PS+312:1,PS+316:22,PS+220:512|64|8});m.update(changes);rows.append(select(ins,m,22)[1])
|
| 29 |
+
write_json(Path('docs/research/qvm-weapon-select.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Complete finite48798 with actual31691/39552/29913 children. Animation duration supplied321 except explicit0/−1; legal selection domain, subsequent completion and original playback unverified.'})
|
| 30 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_weapon_select.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest,copy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_water_events import enqueue
|
| 4 |
+
|
| 5 |
+
def accepted(p):
|
| 6 |
+
a=p['initial'];r=p['requested'];return not (not a['state220']&8 and a['weapon_state']==3 and a['packed']&65280) and 0<r<28 and a['weapon_state']!=2 and not a['state220']&1 and any(v&255==r for v in a['weapons'])
|
| 7 |
+
|
| 8 |
+
def select_state(p):
|
| 9 |
+
a=copy.deepcopy(p['initial'])
|
| 10 |
+
if accepted(p):
|
| 11 |
+
a['queue']=enqueue(a['queue'],[30]);a['weapon_state']=2;a['weapon_timer']=300 if a['packed']&255 else 0;a['torso_timer']=p['duration']&0xffffffff
|
| 12 |
+
if p['kind']<3:a['torso']=((a['torso']&128)^128)|54
|
| 13 |
+
a['counter204']=a['resource']=a['resource_timer']=0;a['state220']&=~(64|8|512)
|
| 14 |
+
return a
|
| 15 |
+
class WeaponSelectTests(unittest.TestCase):
|
| 16 |
+
@classmethod
|
| 17 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-select.json').read_text())
|
| 18 |
+
def test_complete_guard_matrix_and_exact_state(self):
|
| 19 |
+
self.assertEqual(len(self.r['probes']),76)
|
| 20 |
+
for p in self.r['probes']:self.assertEqual(p['final'],select_state(p))
|
| 21 |
+
def test_ordered_children_and_parameter(self):
|
| 22 |
+
for p in self.r['probes']:
|
| 23 |
+
targets=[c['target'] for c in p['execution']['calls']]
|
| 24 |
+
if accepted(p):self.assertEqual(targets,[31691,29913,39552]);self.assertEqual(p['execution']['calls'][1]['args'],{'8':30,'12':0,'16':65536})
|
| 25 |
+
else:self.assertIn(targets,[[],[31691]])
|
| 26 |
+
def test_selection_request_does_not_switch_slot_or_packed_words(self):
|
| 27 |
+
for p in self.r['probes']:self.assertEqual(p['initial']['slot'],p['final']['slot']);self.assertEqual(p['initial']['weapons'],p['final']['weapons']);self.assertEqual(p['initial']['inventory'],p['final']['inventory'])
|
| 28 |
+
def test_state8_overrides_state3_nonempty_magazine_guard(self):
|
| 29 |
+
rows=[p for p in self.r['probes'] if p['initial']['weapon_state']==3 and p['initial']['packed']&65280 and not p['initial']['state220']&1];self.assertTrue(rows)
|
| 30 |
+
for p in rows:self.assertEqual(accepted(p),bool(p['initial']['state220']&8))
|
| 31 |
+
if __name__=='__main__':unittest.main()
|