6.4 KiB
Native RoM NPC equipment pass — 2026-10-03
Scope and behavior
This pass adds source-backed NPC WearEQ effects to the native Runewaker combatant path, capped at the repository's existing supported level 80. It does not connect equipment drops or loot. The WoW path and per-actor combat modes are unchanged.
The export-rom-native-npc-equipment.mjs exporter reads the NPC candidate lists and fixed BowEQ/OrnamentEQ values from npcobject.db, referenced rows from weaponobject.db/armorobject.db, and refine rows from eqrefineabilityobject.db. It preserves all five NPC main-hand/off-hand candidate slots at 20% each, including duplicate and empty choices. Runtime resolution is deterministic and does not average candidate stats:
romNativeNpcCombatant(npcId, runtimeLevel, {
selectionSeed: stableActorOrEntityId,
// Optional source candidate overrides:
mainHandCandidateIndex,
offHandCandidateIndex,
});
Use an actor-unique stable seed when separate live copies of one NPC should roll independently but reproducibly. Omitting the seed defaults to npcId; supplying a candidate index pins that source slot. The resolved equipment and selected candidate indices are included in the combatant result.
The source NPC spawn initialization sets item count to one, durability to 10000, quality to 100, power quality to 10, rare to zero, and item level to min(RefineLv, 20), while selecting the main-hand candidate with rand() % _MAX_NPC_EQSEL_COUNT_ (RD_Setting.cxx:2671–2681). WearEQ computes durability power tiers and applies power quality (RD_ValueCal.cxx:1138–1167), reads weapon attack range/speed and main-hand values (1186–1194), then applies refine rows and the rare-dependent extra-equipment rate (1212–1272). Refine lookup is item refine-table ID plus level minus one (3743+). These source paths drive the exported defaults, equipment choice, wear power, item weapon speed/range, and refine contributions.
Known nonzero WearEqType values are translated only where the existing native snapshot has a source channel: attributes and rates, max HP, physical/magical attack and defense, hit/dodge/critical/parry, main-hand attack speed and damage, body physical damage, magic damage, healing, resistance amounts/rates, and typed magic-power rates. The enum identifiers/order are in ObjectStruct.h:1425+; RoleAttr.cpp verifies base main-hand speed 30, final DMG/MDMG/main damage, and the separate Throw_DMG calculation (184, 288–314, 645, 655, 713, 1201–1204). nativeCombatant.throwDamage carries the derived neutral Throw_DMG input for exact source skill formulas that require it. Unknown nonzero types fail closed. Drop ability IDs/rates are kept as explicitly named drop metadata and are not applied as wearable stats.
Coverage and limits
The imported population has 440 NPC rows. Under the stable default NPC-id seed, 398 resolve to a supported native snapshot. The remaining 42 do not silently receive neutral equipment: 26 selected off-hand weapon damage cases lack a modeled secondary-weapon damage channel; 10 rows have BowEQ/OrnamentEQ channels not represented by the current snapshot; two rows select item 211800 with unknown nonzero WearEqType 0/value 35; and four NPCs (100074, 100594, 100611, 100614) have a vocation-sub-ID mismatch that prevents deriving the existing native stats snapshot.
Across the 112 distinct referenced equipment items, no item ID is missing. There are 96 items with nonzero WearEQ ability entries; 95 have fully mapped active entries, while one has the unresolved type 0 entry above. Referenced source item effect types are 3 (STA), 5 (MND), 8 (MaxHP), 12 (ATK), 13 (DEF), 14 (MDEF), 22 (parry), 25 (DMG), 150 (HealPoint), and 191 (MDMG), in addition to zero/empty slots. The resolver also implements the additional enum channels listed above for source completeness, but does not claim every enum has been validated against a currently equipped item. Twenty-two items have nonzero DropItemAbility metadata; those records are not WearEQ affixes and are not wired here.
The exporter captures refine metadata and the resolver applies a verified refine row sequence when an imported item has a refine level. Current referenced NPC gear has no item with a positive refine level, so the refine application branch is source-implemented but not exercised by present population data. Inherent attributes and runes are empty in the source NPC spawn defaults. Bow/ornament wearable effects and off-hand weapon DMG/speed remain unsupported at combatant construction; no unknown effects are discarded. The source factory's four vocation-sub-ID mismatches remain unchanged by this equipment pass.
The Barren Caves targets are supported with their exact source equipment outcomes:
| NPC | Source equipment | Source durable / attack speed | Resolved wear power |
|---|---|---|---|
| 101459 | Main hand 210056 | 70 / 20 | 1.2 |
| 101460 | Main hand 210061 | 55 / 20 | 1.2 |
| 101461 | Off hand 210666 | 1426 / 20 | 0.2 |
These items have no active WearEQ ability entries, so they preserve the source neutral combat stats while replacing the previous neutral main-hand speed fallback of 30 only when a real source weapon provides its own speed. An independent positive-effect check covers item 210048 (NPC 100606, main-hand candidate 0): source DMG 213 and MDMG 167 contribute at the source 1.2 wear power. NPC 106315/item 213019 also verifies STA, parry and low durability power (0.2).
Generated source snapshot
src/game/generated/romNativeNpcEquipment.generated.json is tied to these local source hashes:
| File | SHA-256 |
|---|---|
npcobject.db |
b627b6eb269c30576a6558000a31abe2db472689ef9a60cc1c1e20207abd69f1 |
armorobject.db |
ccdd438219b801680e4f7c8354171d5347dcab5fcf581f554c39eba3543d961a |
weaponobject.db |
ca58aa607388c4df2fc1a5c6030b1552c2eecb90082fe381cd836aabf6fe467b |
eqrefineabilityobject.db |
724a469d095466df55eb72264ee746a9c6fca866eb76fcb500270cfee3d02f43 |
Validation
Focused tests run:
npx vitest run src/game/romNativeNpcEquipment.test.ts src/game/romNativeCombatResolution.test.ts
16 tests passed
The tests preserve the Barren candidate arrays and weights, check deterministic and explicit candidate selection, check exact item WearEQ power, verify mapped nonzero DMG/MDMG and attributes/parry effects, ensure unknown effects fail closed, and assert that bow/ornament and invalid candidate paths remain unsupported. Broader app validation is handled by the parent integration task.