diff --git a/docs/skill-slots.md b/docs/skill-slots.md index 6910393..e5fb024 100644 --- a/docs/skill-slots.md +++ b/docs/skill-slots.md @@ -1,8 +1,10 @@ # 7 weapon sets and 7 combat-art slots (planned) Goal (user request 2026-10-14): 7 weapon sets and 7 combat-art slots instead -of 5 each; skills on 1-7, weapon sets on F1-F7; old saves keep loading -(migration on read). Status: mapping, nothing changed yet. +of 5 each; skills on 1-7, weapon sets on F1-F7. Inventories are saved as JSON +in place of the binary block; vanilla saves are migrated on load. Saves do not +need to stay loadable by the original game. Status: mapping, nothing changed +yet. ## Where the slots live @@ -51,20 +53,67 @@ flags +0x1162 (2), +0x1164 (2), +0x1166 (2), width (2), height (2), **selected (4)**, +0x1168 (4), quest items (count + 8 each), scrolls (count + 4 each). -Returns the number of bytes processed. Called from `0x5dbd76`, `0x5df00c`, -`0x5df0e5`, `0x5df391`, `0x5df47e`, `0x604a26`, `0x604a7d`, `0x605cee`. +Returns the number of bytes processed. Callers: + +- hero save / load: `ItemDataMgr::saveHero` (`0x6046f0`: `0x604a26`, + `0x604a7d`) and `ItemDataMgr::loadHero` (`0x605900`: `0x605cee`); +- multiplayer: `0x5db670` and `0x5df1f0` serialize into a `cFile_ram` and + send it as a network event; must keep the binary format; +- `0x5def60` (both directions, callers not checked yet). + +### Hero file chunks (`saveHero` / `loadHero`) + +0x100-byte header (magic "AMH" at +0x?, chunk count), then a table of +(type, offset, size) entries, 12 bytes each, 0xc0 bytes = 16 entries, written +last (`saveHero` seeks back to 0x100). `loadHero` seeks to each chunk and +switches on its type; unknown types appear to be skipped (to confirm). + +| Type | Content | +|---|---| +| 0xc0 | hero record (0x5ac) | +| 0xc1 | the hero's inventory (`Serialize`, inventory index = hero id) | +| 0xc6 | inventory #0x11 (`Serialize`; what it is: not checked) | +| 0xc2, 0xc3 (0x22c), 0xc4 (0x40), 0xcc, 0xce, 199, 200 | other hero data | +| 0xca / 0xcb / 0xcd | variants of 0xc1 / 0xc6 / 0xcc with a preceding `0x6076a0` step | + +Decision (2026-10-14): the inventory chunks are written as JSON (new chunk +type) instead of 0xc1/0xc6; reading 0xc1/0xc6 remains as the migration from +vanilla saves. The multiplayer `cFile_ram` path keeps the binary format. ### Slot count by level -`SlotCountForLevel` (`0x57f750`): table at `0x95503c`, (level, count) pairs, -at most 5. Used by the taskbar (`+0x15c`). +`SlotCountForLevel` (`0x57f750`): table at `0x95503c`, (level, count) u16 +pairs: 0 -> 1, 2 -> 2, 8 -> 3, 16 -> 4, 30 -> 5. Used by the taskbar +(`+0x15c`). ### Taskbar (`cUI_Taskbar2`) -Slot controls: weapon sets at +0x164 (5), art slots at +0x178 (5), slot count -at +0x15c; selection highlight written into the controls (+0x54..+0x60, -+0x78) from `0x9ed058`. Layout, rendering (`0x6e3ac0`), hit test (`0x6dfb10`) -and drag & drop of the slots: not mapped yet. +Slot controls (0x7c-byte `UI_STATIC` controls, created in the layout +function `0x6e4ba0`): weapon sets at +0x164 (5), art slots at +0x178 (5); +selection highlight written into the controls (+0x54..+0x60, +0x78) from +`0x9ed058`. The object's fields are packed behind the arrays (+0x18c ...). + +Layout in the 1024 frame, slot width 0x42 (66 px), y = 0xf: + +- weapon sets on the left, growing leftwards from the centre: + x = 0x18a + (i - n) * 0x42 (n = active slots); +- art slots on the right, growing rightwards: x = 0x280 + i * 0x42; +- `SetSlotCount` (`0x6dffa0`, clamps n to 1..5): shows the first n controls + of both rows, moves the weapon row, re-selects if the selection is >= n, + and rebuilds the decorative fill tiles (`UI_STATIC`, random variants from + `0x9eb0b0` / `0x9eb104` / `0x9eb1ac` / `0x9eb200`) between the rows and + the centre (left from 0x16a - n * 0x42, right from 0x23a + n * 0x40). + +With 7 slots the rows reach x = -68 on the left and 1102 on the right, past +the 1024 frame (66-78 px each side). In the widened view the centred bar has +room for that (16:9: 1365 wide, 170 px each side). Decision 2026-10-14: keep +the original spacing; 4:3 is not a target (cut off there). + +The two 5 x 0xbc blocks at +0x258 / +0x604 are the belt (3D item views), not +slots. Still to map: rendering of the slot contents (icons, cooldowns, +`Render` `0x6e3ac0`), drag & drop onto slots (`HandleEvent` mouse branch, +`0x5d9480` / `0x5d9680` calls), and whether the UI manager routes mouse +events outside 0..1024 to the bar. ### Network @@ -80,12 +129,11 @@ only (or disabled in multiplayer). slot variants); the original object keeps slots 1-5 unchanged. 2. Hook the accessors above: index >= 5 goes to the table. Extend the 5-slot loops (`Clear`, `ReplaceItemId`, `FixInventory`, load fix-up). -3. Save: after the original blocks, `Serialize` writes an extension block - (magic + version + slots 6-7). Reading checks for the magic; a save - without it (old save) gets empty slots 6-7 = the migration. Open: whether - the stream can peek / seek back, and whether the caller stores the - returned length. -4. Slot count table: 7 entries (unlock levels for slots 6 and 7 to choose). +3. Save: `saveHero` writes the inventories as a JSON chunk (versioned) + instead of 0xc1 / 0xc6; `loadHero` reads the JSON chunk, or migrates a + vanilla 0xc1 / 0xc6 chunk through the original reader (slots 6-7 empty). +4. Slot count table: 7 entries; slot 6 at level 45, slot 7 at level 65 + (agreed 2026-10-14). 5. Taskbar: 7 controls each (arrays inside the object are fixed at 5: own table for the extra controls), layout, rendering, hit tests, drag & drop. 6. Keys: 1-7 select and cast art slots, F1-F7 select weapon sets.