import { describe, expect, it } from 'vitest' import { createOnlineHudSession, D2StatId, extractStatStrings, layoutGroundItemLabels, makeItemData, readU16LE, readU32LE, resolveGroundItemMetadata, } from './helpers.ts' describe('Tier 3 — Pairwise Cross-Feature Integration Coverage (16 Tests)', () => { it('CF1 (F1 × F3): identifying an unidentified Unique item via 0x27 IdentifyItem + S2C 0x9C reveals canonical 1.13c stats', () => { const { world, hud, hudModel, sentCommands, sentPackets } = createOnlineHudSession() const tome = makeItemData({ id: 4001, code: 'ibk', location: 0, container: 'inventory', gridX: 0, gridY: 0, invWidth: 1, invHeight: 2, }) const unidAxe = makeItemData({ id: 4002, code: 'hax', quality: 7, uniqueItemId: 0, // The Gnasher identified: false, location: 0, container: 'inventory', gridX: 2, gridY: 0, invWidth: 1, invHeight: 3, }) world.apply({ type: 'ItemEvent', item: tome }) world.apply({ type: 'ItemEvent', item: unidAxe }) hudModel.syncFromWorld() expect(hud.inventory.inventory.find((i) => i.id === 4002)?.identified).toBe(false) ;(hudModel as Record).identifyItem(4002, 4001) expect(sentCommands).toContainEqual({ type: 'IdentifyItem', itemId: 4002, scrollId: 4001 }) expect(sentPackets[0]![0]).toBe(0x27) // Server responds with identified item and canonical 1.13c stats world.apply({ type: 'ItemEvent', item: makeItemData({ ...unidAxe, action: 0x02, identified: true, stats: [ { statId: 17, param: 0, value: 65 }, { statId: 18, param: 0, value: 65 }, { statId: 135, param: 0, value: 50 }, ], }), }) hudModel.syncFromWorld() const identifiedAxe = hud.inventory.inventory.find((i) => i.id === 4002)! expect(identifiedAxe.identified).toBe(true) const statsText = extractStatStrings(identifiedAxe.stats).join('\n') expect(statsText).not.toMatch(/Stat #\d+/i) expect(statsText).toMatch(/65%|50%/) }) it('CF2 (F1 × F3): socketing a Rune into a socketed helm via 0x28 SocketItem + S2C 0x9D updates socketedItems and tooltip', () => { const { world, hud, hudModel, sentCommands, sentPackets } = createOnlineHudSession() const helm = makeItemData({ id: 4010, code: 'cap', location: 0, container: 'inventory', gridX: 0, gridY: 0, invWidth: 2, invHeight: 2, socketed: true, totalSockets: 2, usedSockets: 0, stats: [{ statId: D2StatId.ARMORCLASS, param: 0, value: 5 }], }) const tirRune = makeItemData({ id: 4011, code: 'r03', location: 4, container: 'cursor', }) world.apply({ type: 'ItemEvent', item: helm }) world.apply({ type: 'ItemEvent', item: tirRune }) hudModel.syncFromWorld() ;(hudModel as Record).socketItem(4011, 4010) expect(sentCommands).toContainEqual({ type: 'SocketItem', itemId: 4011, targetItemId: 4010 }) expect(sentPackets[0]![0]).toBe(0x28) // Server confirms socket insertion world.apply({ type: 'ItemEvent', item: makeItemData({ ...helm, usedSockets: 1, socketedChildCount: 1 }), }) world.apply({ type: 'ItemEvent', item: makeItemData({ ...tirRune, opcode: 0x9d, action: 0x0b, location: 6, container: 'socket', inSocket: true, ownerType: 4, ownerId: 4010, gridX: 0, }), }) hudModel.syncFromWorld() const syncedHelm = hud.inventory.inventory.find((i) => i.id === 4010)! expect(syncedHelm.usedSockets).toBe(1) expect(syncedHelm.socketedItems?.length).toBe(1) }) it('CF3 (F2 × F3): packing Scroll of Identify (isc) into Tome of Identify (ibk) emits 0x29 ScrollToBook and syncs tome quantity', () => { const { world, hudModel, sentCommands, sentPackets } = createOnlineHudSession() const tome = makeItemData({ id: 4020, code: 'ibk', location: 0, container: 'inventory', gridX: 0, gridY: 0, quantity: 5, stats: [{ statId: D2StatId.QUANTITY, param: 0, value: 5 }], }) const scroll = makeItemData({ id: 4021, code: 'isc', location: 4, container: 'cursor', }) world.apply({ type: 'ItemEvent', item: tome }) world.apply({ type: 'ItemEvent', item: scroll }) hudModel.syncFromWorld() ;(hudModel as Record).scrollToBook(4021, 4020) expect(sentCommands).toContainEqual({ type: 'ScrollToBook', scrollId: 4021, bookId: 4020 }) expect(sentPackets[0]![0]).toBe(0x29) expect(readU32LE(sentPackets[0]!, 1)).toBe(4021) expect(readU32LE(sentPackets[0]!, 5)).toBe(4020) }) it('CF4 (F1 × F4): equipping Rare/Unique weapon in Weapon Set II (bodyLoc 11 & 12) syncs 1.13c stats across weapon switch', () => { const { world, hud, hudModel } = createOnlineHudSession() world.apply({ type: 'ItemEvent', item: makeItemData({ id: 4030, code: 'bsd', quality: 6, rareName1: 3, rareName2: 8, location: 1, container: 'equipped', bodyLoc: 11, equippedSlot: 11, stats: [ { statId: 17, param: 0, value: 80 }, { statId: 18, param: 0, value: 80 }, ], }), }) hudModel.syncFromWorld() expect(hud.inventory.swapWeapons.weapon1).not.toBeNull() expect(hud.inventory.swapWeapons.weapon1?.quality).toBe('rare') const statLines = extractStatStrings(hud.inventory.swapWeapons.weapon1?.stats).join('\n') expect(statLines).not.toMatch(/Stat #\d+/i) expect(statLines).toMatch(/80%/) world.apply({ type: 'WeaponSwitch' }) hudModel.syncFromWorld() expect(hud.inventory.activeWeaponSlot).toBe(1) expect(hud.inventory.swapWeapons.weapon1?.id).toBe(4030) }) it('CF5 (F5 × F6): Deckard Cain (hcIdx 244) interaction via 0x59 + S2C 0x27/0x91 opens Cain menu and Talk sends 0x31 QuestMessage', () => { const { world, worldPanels, hudModel, sentCommands, sentPackets } = createOnlineHudSession() world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4040, classId: 244, name: 'Deckard Cain', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4040 } }) world.apply({ type: 'Npc', kind: 'info', unitType: 1, unitId: 4040, raw: new Uint8Array(40), }) world.apply({ type: 'Npc', kind: 'menu', npcUnitId: 4040, raw: new Uint8Array(26), }) hudModel.syncFromWorld() const optIds = worldPanels.npcMenu?.options.map((o) => o.id) ?? [] expect(optIds).toContain('identify') expect(optIds).toContain('talk') worldPanels.clickNpcMenuOption('talk') if (!sentCommands.some((c) => c.type === 'QuestMessage')) { const subTopic = worldPanels.npcMenu?.options.find((o) => String(o.id).startsWith('talk_')) if (subTopic) worldPanels.clickNpcMenuOption(subTopic.id) } expect(sentCommands.some((c) => c.type === 'QuestMessage')).toBe(true) expect(sentPackets.some((p) => p[0] === 0x31 && p.byteLength === 9)).toBe(true) }) it('CF6 (F5 × F7): Warriv Act 1 (155) travel_east (0x38) -> S2C LoadAct (Act 2) -> Warriv Act 2 (175) travel_west', () => { const { world, worldPanels, hudModel, sentCommands, sentPackets, clearRecorded } = createOnlineHudSession({ act: 0, areaId: 1 }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4050, classId: 155, name: 'Warriv', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4050 } }) hudModel.syncFromWorld() worldPanels.clickNpcMenuOption('travel_east' as any) expect(sentCommands).toContainEqual( expect.objectContaining({ type: 'NpcEntityAction', npcId: 4050 }), ) expect(sentPackets.some((p) => p[0] === 0x38 && p.byteLength === 13)).toBe(true) // Server transitions player to Act 2 Lut Gholein clearRecorded() world.apply({ type: 'LoadAct', act: 1, mapSeed: 0x2222, areaId: 40, objectSeed: 0x3333 }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4051, classId: 175, name: 'Warriv', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4051 } }) hudModel.syncFromWorld() expect(worldPanels.npcMenu?.options.some((o) => o.id === ('travel_west' as any))).toBe(true) worldPanels.clickNpcMenuOption('travel_west' as any) expect(sentCommands).toContainEqual( expect.objectContaining({ type: 'NpcEntityAction', npcId: 4051 }), ) }) it('CF7 (F8 × F9): Kashya (150) hire menu -> 0x38 (action=3) -> S2C 0x4F/0x4E candidates -> 0x36 MercHire -> 0x81 AssignMerc', () => { const { world, worldPanels, hudModel, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4060, classId: 150, name: 'Kashya', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4060 } }) hudModel.syncFromWorld() worldPanels.clickNpcMenuOption('hire' as any) expect(sentCommands).toContainEqual( expect.objectContaining({ type: 'NpcEntityAction', action: 3, npcId: 4060 }), ) world.apply({ type: 'Merc', kind: 'hireListStart' }) world.apply({ type: 'Merc', kind: 'forHire', nameId: 7, seed: 0xabcdef }) hudModel.syncFromWorld() ;(hudModel as Record).hireMerc(4060, 7) expect(sentCommands).toContainEqual({ type: 'MercHire', npcId: 4060, mercId: 7 }) expect(sentPackets.some((p) => p[0] === 0x36 && p.byteLength === 9)).toBe(true) world.apply({ type: 'Merc', kind: 'assign', ownerId: 1, mercId: 500, classId: 271, nameId: 7, }) hudModel.syncFromWorld() expect(world.merc?.mercId).toBe(500) }) it('CF8 (F8 × F9): mercenary lethal Damage (0x0C) + 0x9B MercReviveCost enables Greiz (198) resurrect -> 0x62 MercResurrect', () => { const { world, worldPanels, hudModel, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, }) world.apply({ type: 'Merc', kind: 'assign', ownerId: 1, mercId: 500, classId: 338 }) world.apply({ type: 'Damage', unitType: 1, unitId: 500, lifePct: 0, raw: new Uint8Array(9), }) world.apply({ type: 'Merc', kind: 'reviveCost', deadNameId: 12, reviveCost: 11000 }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4070, classId: 198, name: 'Greiz', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4070 } }) hudModel.syncFromWorld() expect(worldPanels.npcMenu?.options.some((o) => o.id === ('resurrect' as any))).toBe(true) worldPanels.clickNpcMenuOption('resurrect' as any) expect(sentCommands).toContainEqual({ type: 'MercResurrect', npcId: 4070 }) expect(sentPackets.some((p) => p[0] === 0x62 && p.byteLength === 5)).toBe(true) }) it('CF9 (F1 × F10): equipping 1.13c items onto mercenary (world.containers.merc) syncs tooltips in O paperdoll and emits 0x61 MercItem', () => { const { world, hud, hudModel, dom, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, }) world.apply({ type: 'Merc', kind: 'assign', ownerId: 1, mercId: 500, classId: 338 }) world.apply({ type: 'ItemEvent', item: makeItemData({ id: 4080, opcode: 0x9d, action: 0x06, code: 'scy', // Scythe quality: 4, location: 1, container: 'merc', bodyLoc: 4, equippedSlot: 4, stats: [ { statId: 17, param: 0, value: 50 }, { statId: 18, param: 0, value: 50 }, ], }), }) hudModel.syncFromWorld() dom.dispatchKeyDown('o') const mercState = (hud as Record).merc ?? (hud.worldPanels as Record).merc const weapon = mercState?.equipped?.weapon1 ?? mercState?.equipped?.rightHand ?? mercState?.weapon1 expect(weapon).toBeDefined() expect(weapon.twoHandDamage).toBeDefined() ;(hudModel as Record).mercItem(4) expect(sentCommands).toContainEqual({ type: 'MercItem', slot: 4 }) expect(sentPackets.some((p) => p[0] === 0x61 && p.byteLength === 3)).toBe(true) }) it('CF10 (F3 × F8 × F10): Shift+useBeltSlot dispatches 0x26 BeltUse (useOnMerc=1) and S2C 0xAB NPCHeal restores merc portrait lifePct', () => { const { world, hudModel, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1 }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 500, classId: 338, name: 'Emilio', x: 5102, y: 5052, lifePct: 40 }, }) world.apply({ type: 'Merc', kind: 'assign', ownerId: 1, mercId: 500, classId: 338 }) world.apply({ type: 'Damage', unitType: 1, unitId: 500, lifePct: 40, raw: new Uint8Array(9), }) world.apply({ type: 'ItemEvent', item: makeItemData({ id: 4090, code: 'hp4', location: 2, container: 'belt', gridX: 0, x: 0, }), }) hudModel.syncFromWorld() hudModel.useBeltSlot(0, true) expect(sentCommands).toContainEqual({ type: 'BeltUse', itemId: 4090, useOnMerc: 1 }) expect(sentPackets[0]![0]).toBe(0x26) expect(readU32LE(sentPackets[0]!, 5)).toBe(1) world.apply({ type: 'Npc', kind: 'heal', unitType: 1, unitId: 500, lifePct: 128 }) hudModel.syncFromWorld() expect((world.merc as Record).lifePct).toBe(128) }) it('CF11 (F11 × F12): clicking dungeon warp (0x04 + 0x09) and spawning Town Portal (0x60/0x82) resolves warp and portal hover labels', () => { const { world, onlineSession, commandMapper, sentCommands, sentPackets } = createOnlineHudSession({ x: 5100, y: 5050 }) world.apply({ type: 'UnitAssign', unit: { unitType: 5, id: 4100, classId: 4, warpId: 4, name: 'To Den of Evil', x: 5103, y: 5053, } as any, }) const warpUnit = world.getUnit('tile', 4100)! commandMapper.triggerPrimaryWorldAction(5103, 5053, warpUnit, 5000) expect(sentCommands.some((c) => c.type === 'MoveToUnit')).toBe(true) expect(sentPackets.some((p) => p[0] === 0x09 && p.byteLength === 9)).toBe(true) world.apply({ type: 'UnitAssign', unit: { unitType: 2, id: 4101, classId: 59, name: 'Town Portal', x: 5105, y: 5055 }, }) world.apply({ type: 'Portal', kind: 'ownership', ownerId: 1, ownerName: 'Hero', localId: 4101, remoteId: 4102, }) const portalRenderable = onlineSession.buildRenderableUnits().find((u) => u.id === 4101) expect(portalRenderable?.name).toContain("Hero's Town Portal") }) it('CF12 (F1 × F12): ground item drop (750 Gold + Unique Cap) -> Alt label layout -> 0x16 ItemPickup -> S2C 0x9C inventory tooltip', () => { const { world, hud, hudModel, commandMapper, sentCommands } = createOnlineHudSession() const groundGold = makeItemData({ id: 4110, action: 0x01, code: 'gld', location: 3, container: 'ground', worldX: 5102, worldY: 5052, x: 5102, y: 5052, quantity: 750, }) const groundCap = makeItemData({ id: 4111, action: 0x01, code: 'cap', quality: 7, uniqueItemId: 0, // Biggin's Bonnet location: 3, container: 'ground', worldX: 5102, worldY: 5052, x: 5102, y: 5052, invWidth: 2, invHeight: 2, }) world.apply({ type: 'ItemEvent', item: groundGold }) world.apply({ type: 'ItemEvent', item: groundCap }) const metaGold = resolveGroundItemMetadata({ id: 4110, code: 'gld', goldAmount: 750 }) const metaCap = resolveGroundItemMetadata({ id: 4111, code: 'cap', quality: 7 }) const boxes = layoutGroundItemLabels([ { id: 4110, screenX: 400, screenY: 300, label: metaGold.labelEn, color: metaGold.color }, { id: 4111, screenX: 400, screenY: 300, label: metaCap.labelEn, color: metaCap.color }, ]) expect(boxes.length).toBe(2) expect(boxes[0]!.y).not.toBe(boxes[1]!.y) const capUnit = world.getUnit('item', 4111)! commandMapper.triggerPrimaryWorldAction(5102, 5052, capUnit, 6000) expect(sentCommands).toContainEqual({ type: 'ItemPickup', itemId: 4111, toCursor: false }) // Server moves Cap into player's inventory world.apply({ type: 'ItemEvent', item: makeItemData({ ...groundCap, action: 0x00, location: 0, container: 'inventory', gridX: 0, gridY: 0, stats: [{ statId: D2StatId.MAXHP, param: 0, value: 15 << 8 }], }), }) hudModel.syncFromWorld() expect(hud.inventory.inventory.find((i) => i.id === 4111)?.quality).toBe('unique') }) it('CF13 (F13 × F14): monster SkillCast (0x6C) + lethal PlayerLifeManaChange (0x95) triggers death overlay and Escape sends 0x41 Resurrect', () => { const { world, hudModel, commandMapper, renderFrame, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, life: 100, maxLife: 100 }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4120, classId: 1, name: 'Fallen', x: 5105, y: 5055 }, }) world.apply({ type: 'SkillCast', kind: 'monsterAttack', monsterId: 4120, attackType: 0, targetId: 1, targetType: 0, target: { x: 5100, y: 5050 }, }) world.apply({ type: 'PlayerLifeManaChange', life: 0, mana: 20, stamina: 40, x: 5100, y: 5050, vx: 0, vy: 0, }) hudModel.syncFromWorld() const ctx = renderFrame() expect(ctx.drawnTexts.map((t) => t.text).join('\n')).toMatch(/You Have Died|你挂了|Press ESC/i) commandMapper.handleKeyDown({ key: 'Escape' }) expect(sentCommands).toContainEqual({ type: 'Resurrect' }) expect(sentPackets.some((p) => p[0] === 0x41 && p.byteLength === 1)).toBe(true) }) it('CF14 (F8 × F14): simultaneous mercenary death (0x9B) and player death -> Resurrect (0x41) -> Qual-Kehk (515) MercResurrect (0x62)', () => { const { world, worldPanels, hudModel, commandMapper, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, life: 100, maxLife: 100 }) world.apply({ type: 'Merc', kind: 'assign', ownerId: 1, mercId: 500, classId: 561 }) world.apply({ type: 'Merc', kind: 'reviveCost', deadNameId: 40, reviveCost: 20000 }) world.apply({ type: 'PlayerLifeManaChange', life: 0, mana: 0, stamina: 0, x: 5100, y: 5050, vx: 0, vy: 0, }) hudModel.syncFromWorld() commandMapper.handleKeyDown({ key: 'Escape' }) expect(sentCommands).toContainEqual({ type: 'Resurrect' }) // Server respawns player in Harrogath with life=100 world.apply({ type: 'PlayerLifeManaChange', life: 100, mana: 80, stamina: 100, x: 5100, y: 5050, vx: 0, vy: 0, }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4130, classId: 515, name: 'Qual-Kehk', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4130 } }) hudModel.syncFromWorld() worldPanels.clickNpcMenuOption('resurrect' as any) expect(sentCommands).toContainEqual({ type: 'MercResurrect', npcId: 4130 }) expect(sentPackets.some((p) => p[0] === 0x62 && p.byteLength === 5)).toBe(true) }) it('CF15 (F12 × F15): allied party member joins (0x5B/0x8B/0x7F/0x90) and spawns Town Portal (0x82) with ownerName label', () => { const { world, onlineSession, hudModel } = createOnlineHudSession({ selfId: 1 }) world.apply({ type: 'Party', kind: 'roster', playerId: 2, charClass: 1, charLevel: 35, partyId: 1, charName: 'Jaina', }) world.apply({ type: 'Party', kind: 'relation', subKind: 'partyState', playerId: 2, state: 1, }) world.apply({ type: 'Party', kind: 'memberState', isSelf: false, unitId: 2, lifePct: 120, areaId: 2, }) world.apply({ type: 'Party', kind: 'memberPos', playerId: 2, at: { x: 5120, y: 5070 }, }) world.apply({ type: 'UnitAssign', unit: { unitType: 2, id: 4140, classId: 59, name: 'Town Portal', x: 5108, y: 5058 }, }) world.apply({ type: 'Portal', kind: 'ownership', ownerId: 2, ownerName: 'Jaina', localId: 4140, remoteId: 4141, }) hudModel.syncFromWorld() expect(world.party.get(2)?.charName).toBe('Jaina') const portal = onlineSession.buildRenderableUnits().find((u) => u.id === 4140) expect(portal?.name).toContain("Jaina's Town Portal") }) it('CF16 (F1 × F5 × F16): player trade session (0x77/0x78/0x79 -> 0x4F) followed by Larzuk (511) add_sockets quest reward panel', () => { const { world, worldPanels, hudModel, sentCommands, sentPackets } = createOnlineHudSession({ selfId: 1, }) world.apply({ type: 'Trade', kind: 'button', action: 0x01 }) world.apply({ type: 'Trade', kind: 'accepted', partnerName: 'TraderJoe', partnerId: 2, } as any) world.apply({ type: 'ItemEvent', item: makeItemData({ id: 4150, code: 'bsd', quality: 2, location: 0, container: 'tradePartner', storePage: 2, storagePage: 2, gridX: 0, gridY: 0, }), }) hudModel.syncFromWorld() expect(world.trade.active).toBe(true) if (typeof (hudModel as Record).clickButton === 'function') { ;(hudModel as Record).clickButton(0x04, 0) } else { ;(hudModel as Record).sendCommand?.({ type: 'ClickButton', button: 0x04, buttonId: 0x04, complement: 0, }) } expect(sentPackets.some((p) => p[0] === 0x4f && readU16LE(p, 1) === 0x04)).toBe(true) // Trade completes (0x06) and item lands in player's inventory world.apply({ type: 'Trade', kind: 'button', action: 0x06 }) world.apply({ type: 'ItemEvent', item: makeItemData({ id: 4150, code: 'bsd', quality: 2, location: 0, container: 'inventory', storePage: 0, storagePage: 0, gridX: 0, gridY: 0, }), }) world.apply({ type: 'UnitAssign', unit: { unitType: 1, id: 4151, classId: 511, name: 'Larzuk', x: 5104, y: 5054 }, }) world.noteOutgoingCommand({ type: 'NpcInit', target: { unitType: 1, id: 4151 } }) hudModel.syncFromWorld() expect(worldPanels.npcMenu?.options.some((o) => o.id === ('add_sockets' as any))).toBe(true) worldPanels.clickNpcMenuOption('add_sockets' as any) expect(sentCommands.length).toBeGreaterThan(0) }) })