qvm: wound gates and meter increments
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_DAMAGE_WOUNDS.md +7 -0
- docs/research/README.md +3 -0
- docs/research/qvm-damage-wounds.json +0 -0
- scripts/analyze_qvm_damage_wounds.py +16 -0
- scripts/qvm_damage_support.py +2 -2
- scripts/test_qvm_damage_wounds.py +26 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -784,3 +784,6 @@ Research progress: RE-705 [report](research/QVM_DAMAGE_ATTENUATION.md); remainin
|
|
| 784 |
|
| 785 |
|
| 786 |
Research progress: RE-706 [report](research/QVM_DAMAGE_CLAMP.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
|
| 785 |
|
| 786 |
Research progress: RE-706 [report](research/QVM_DAMAGE_CLAMP.md); remaining native/content/runtime/platform scope stays open.
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
Research progress: RE-707 [report](research/QVM_DAMAGE_WOUNDS.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -1130,3 +1130,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 1130 |
|
| 1131 |
|
| 1132 |
**RE-706 — Damage saturation and weapon4 accumulation:** Positive damage factors cap at1 and weapon4 additionally caps at float32 0.35; its optional accumulator adds that capped factor without a total clamp. Nonpositive factors return zero immediately. [Evidence](research/QVM_DAMAGE_CLAMP.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1130 |
|
| 1131 |
|
| 1132 |
**RE-706 — Damage saturation and weapon4 accumulation:** Positive damage factors cap at1 and weapon4 additionally caps at float32 0.35; its optional accumulator adds that capped factor without a total clamp. Nonpositive factors return zero immediately. [Evidence](research/QVM_DAMAGE_CLAMP.md).
|
| 1133 |
+
|
| 1134 |
+
|
| 1135 |
+
**RE-707 — Wound eligibility and client meter increments:** Mode9, entity flag16 or protection suppress the wound word. Active client wounds set leg flag128 at locations11–14 and increment client4312 with source-specific threshold rules. [Evidence](research/QVM_DAMAGE_WOUNDS.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1119,3 +1119,6 @@ Research progress: RE-705 [report](research/QVM_DAMAGE_ATTENUATION.md); remainin
|
|
| 1119 |
|
| 1120 |
|
| 1121 |
Research progress: RE-706 [report](research/QVM_DAMAGE_CLAMP.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 1119 |
|
| 1120 |
|
| 1121 |
Research progress: RE-706 [report](research/QVM_DAMAGE_CLAMP.md); remaining native/content/runtime/platform scope stays open.
|
| 1122 |
+
|
| 1123 |
+
|
| 1124 |
+
Research progress: RE-707 [report](research/QVM_DAMAGE_WOUNDS.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -1172,3 +1172,8 @@ Weapon24 preserves the factor below half range, linearly falls to float32 0.1 by
|
|
| 1172 |
## 2026-10-21 — RE-706 Damage saturation and weapon4 accumulation (separate task commit)
|
| 1173 |
|
| 1174 |
Positive damage factors cap at1 and weapon4 additionally caps at float32 0.35; its optional accumulator adds that capped factor without a total clamp. Nonpositive factors return zero immediately. 144 finite factor/weapon/pointer/accumulator combinations pass exact saturation and additive oracles. Full suite: 1275 tests pass. Report regenerates byte-identically (215368 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1172 |
## 2026-10-21 — RE-706 Damage saturation and weapon4 accumulation (separate task commit)
|
| 1173 |
|
| 1174 |
Positive damage factors cap at1 and weapon4 additionally caps at float32 0.35; its optional accumulator adds that capped factor without a total clamp. Nonpositive factors return zero immediately. 144 finite factor/weapon/pointer/accumulator combinations pass exact saturation and additive oracles. Full suite: 1275 tests pass. Report regenerates byte-identically (215368 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 1175 |
+
|
| 1176 |
+
|
| 1177 |
+
## 2026-10-21 — RE-707 Wound eligibility and client meter increments (separate task commit)
|
| 1178 |
+
|
| 1179 |
+
Mode9, entity flag16 or protection suppress the wound word. Active client wounds set leg flag128 at locations11–14 and increment client4312 with source-specific threshold rules. 302 eligibility/weapon/location/ratio/meter fixtures pass independent exact float32 and unchanged-state checks. Full suite: 1279 tests pass. Report regenerates byte-identically (464742 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
|
@@ -1162,3 +1162,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1162 |
|
| 1163 |
|
| 1164 |
- [x] RE-706 Damage saturation and weapon4 accumulation ([report](research/QVM_DAMAGE_CLAMP.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1162 |
|
| 1163 |
|
| 1164 |
- [x] RE-706 Damage saturation and weapon4 accumulation ([report](research/QVM_DAMAGE_CLAMP.md)).
|
| 1165 |
+
|
| 1166 |
+
|
| 1167 |
+
- [x] RE-707 Wound eligibility and client meter increments ([report](research/QVM_DAMAGE_WOUNDS.md)).
|
docs/research/QVM_DAMAGE_WOUNDS.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Wound eligibility and client meter increments
|
| 2 |
+
|
| 3 |
+
RE-707. Reproduce with `python3 scripts/analyze_qvm_damage_wounds.py`; check with `python3 scripts/test_qvm_damage_wounds.py`. The [machine report](qvm-damage-wounds.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
Actual290002–290227 suppresses the selected wound word for mode9, entity552 bit16 or protected local28. Null clients or a zero wound word bypass attribution at290298. For weapon4, ratio below float32(0.15) increments client4312 by float32(0.1), and locations11–14 set client220 bit128; ratios at/above threshold skip those two effects. Other weapons add1 for leg locations when the meter is below1, otherwise float32(0.6). Nonleg locations add0.5 below0.5, otherwise float32(0.6). No cap occurs here. Meter semantics remain provisional. Attribution/counters and health damage are not executed by this bounded stage.
|
| 6 |
+
|
| 7 |
+
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
|
@@ -720,3 +720,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 720 |
|
| 721 |
|
| 722 |
[RE-706: Damage saturation and weapon4 accumulation](QVM_DAMAGE_CLAMP.md). Reproduce with `python3 scripts/analyze_qvm_damage_clamp.py`; check with `python3 scripts/test_qvm_damage_clamp.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 720 |
|
| 721 |
|
| 722 |
[RE-706: Damage saturation and weapon4 accumulation](QVM_DAMAGE_CLAMP.md). Reproduce with `python3 scripts/analyze_qvm_damage_clamp.py`; check with `python3 scripts/test_qvm_damage_clamp.py`.
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
[RE-707: Wound eligibility and client meter increments](QVM_DAMAGE_WOUNDS.md). Reproduce with `python3 scripts/analyze_qvm_damage_wounds.py`; check with `python3 scripts/test_qvm_damage_wounds.py`.
|
docs/research/qvm-damage-wounds.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_damage_wounds.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Actual wound eligibility, leg flag and client4312 accumulation."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source
|
| 6 |
+
from qvm_damage_support import seed,run
|
| 7 |
+
from analyze_qvm import write_json
|
| 8 |
+
|
| 9 |
+
def main():
|
| 10 |
+
q,raw,h,ins=source();rows=[]
|
| 11 |
+
cases=[dict(mode=mode,entity_flags=flags,protected=protected,client=client,wound=wound,location=11) for mode,flags,protected,client,wound in itertools.product([3,9],[0,16],[0,1],[False,True],[0,1])]
|
| 12 |
+
cases += [dict(weapon=w,location=loc,stamina=stamina,ratio=ratio) for w,loc,stamina,ratio in itertools.product([3,4,24],[1,10,11,12,13,14],[0,0.49,0.5,0.99,1],[0.149,0.15,0.151])]
|
| 13 |
+
for a in cases:
|
| 14 |
+
_,p=run(ins,seed(raw,h,**a),290002,290298,boundaries=[290228]);p['input']=a;rows.append(p)
|
| 15 |
+
write_json(Path('docs/research/qvm-damage-wounds.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual290002..290227 with explicit initial factor locals. Stops before attribution or at bypass290298. Client4312 semantic name remains provisional; no health damage.'})
|
| 16 |
+
if __name__=='__main__':main()
|
scripts/qvm_damage_support.py
CHANGED
|
@@ -20,12 +20,12 @@ def seed(raw,h,**x):
|
|
| 20 |
if slot>=0:m[CLIENT+376+4*slot]=0xaa0000|item
|
| 21 |
return m
|
| 22 |
|
| 23 |
-
def run(ins,m,start,stop):
|
| 24 |
notices=[]
|
| 25 |
def inventory(i,m,a):
|
| 26 |
m,_,v,r=inventory_check(i,m,a,(0,0,0));return m,v,r
|
| 27 |
before=snapshot(m)
|
| 28 |
-
m,e=resume(ins,start,stop,m,L,handlers={32155:inventory,278003:captured(278003,notices)})
|
| 29 |
return m,{'initial':before,'final':snapshot(m),'notices':notices,'execution':e}
|
| 30 |
|
| 31 |
def snapshot(m):
|
|
|
|
| 20 |
if slot>=0:m[CLIENT+376+4*slot]=0xaa0000|item
|
| 21 |
return m
|
| 22 |
|
| 23 |
+
def run(ins,m,start,stop,boundaries=()):
|
| 24 |
notices=[]
|
| 25 |
def inventory(i,m,a):
|
| 26 |
m,_,v,r=inventory_check(i,m,a,(0,0,0));return m,v,r
|
| 27 |
before=snapshot(m)
|
| 28 |
+
m,e=resume(ins,start,stop,m,L,handlers={32155:inventory,278003:captured(278003,notices)},boundaries=boundaries)
|
| 29 |
return m,{'initial':before,'final':snapshot(m),'notices':notices,'execution':e}
|
| 30 |
|
| 31 |
def snapshot(m):
|
scripts/test_qvm_damage_wounds.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_ground import word,n,f
|
| 4 |
+
class DamageWoundTests(unittest.TestCase):
|
| 5 |
+
@classmethod
|
| 6 |
+
def setUpClass(cls):cls.ps=json.loads(Path('docs/research/qvm-damage-wounds.json').read_text())['probes']
|
| 7 |
+
def test_independent_wound_and_leg_flag_rules(self):
|
| 8 |
+
for p in self.ps:
|
| 9 |
+
a=p['input'];wound=a.get('wound',1) if a.get('mode',3)!=9 and not a.get('entity_flags',0)&16 and not a.get('protected',0) else 0
|
| 10 |
+
active=bool(wound and a.get('client',True));leg=a.get('location',3) in (11,12,13,14);eligible=a.get('weapon',3)!=4 or f(a.get('ratio',0))<n(1041865114)
|
| 11 |
+
self.assertEqual(p['final']['locals']['24'],wound);self.assertEqual(p['final']['client']['220'],8|(128 if active and leg and eligible else 0));self.assertEqual(p['execution']['stop'],290228 if active else 290298)
|
| 12 |
+
def test_exact_accumulation_thresholds(self):
|
| 13 |
+
for p in self.ps:
|
| 14 |
+
a=p['input'];v=f(a.get('stamina',0));active=bool(p['final']['locals']['24'] and a.get('client',True));w=a.get('weapon',3)
|
| 15 |
+
if active and w==4 and f(a.get('ratio',0))<n(1041865114):v=f(v+n(1036831949))
|
| 16 |
+
elif active and w!=4:
|
| 17 |
+
threshold=1. if a.get('location',3) in (11,12,13,14) else 0.5;v=f(v+(threshold if v<threshold else n(1058642330)))
|
| 18 |
+
self.assertEqual(p['final']['client']['4312'],word(v))
|
| 19 |
+
def test_no_attribution_or_counter_changes_yet(self):
|
| 20 |
+
for p in self.ps:
|
| 21 |
+
for o in ['4300','4304','4316','6304']:self.assertEqual(p['final']['client'][o],p['initial']['client'][o])
|
| 22 |
+
self.assertEqual(p['final']['attacker_pointer'],0)
|
| 23 |
+
def test_no_child_calls(self):
|
| 24 |
+
self.assertEqual(len(self.ps),302)
|
| 25 |
+
for p in self.ps:self.assertEqual(p['execution']['calls'],[])
|
| 26 |
+
if __name__=='__main__':unittest.main()
|