diablo2-web/tests/p0-522-ground-items.test.ts

73 lines
2.6 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { GameEngine } from '../src/game/engine.ts'
import type { Item } from '../src/game/items.ts'
function makeTestItem(id: number, code: string, name: string): Item {
return {
id,
code,
name,
type: 'misc',
quality: 'normal',
level: 1,
identified: true,
invWidth: 1,
invHeight: 1,
stats: [],
baseCost: 50,
} as unknown as Item
}
describe('Issue #522: Ground Items Single Source of Truth (engine.groundItems)', () => {
it('keeps engine.ground strictly synchronized as a read-only view over engine.groundItems', () => {
const engine = new GameEngine('nec')
const itemA = makeTestItem(1, 'hp1', 'Minor Healing Potion')
const itemB = makeTestItem(2, 'mp1', 'Minor Mana Potion')
const gA = engine.dropItemToGround(itemA, 100, 100)
const gB = engine.dropItemToGround(itemB, 108, 108)
expect(engine.groundItems.count).toBe(2)
expect(engine.ground.length).toBe(2)
expect(engine.ground.map(g => g.id)).toEqual([gA.id, gB.id])
// Picking up gA by exact ID removes only gA and preserves nearby gB
const picked = engine.pickupGroundEntity(gA.id)
expect(picked).toBe(true)
expect(engine.groundItems.count).toBe(1)
expect(engine.ground.length).toBe(1)
expect(engine.ground[0]!.id).toBe(gB.id)
expect(engine.bag.items.some(p => p.item.id === itemA.id)).toBe(true)
})
it('saves and restores ground items without phantom duplicates on level transition', () => {
const engine = new GameEngine('dru')
const item1 = makeTestItem(10, 'rvl', 'Full Rejuvenation Potion')
engine.dropItemToGround(item1, 200, 200)
engine.groundItems.spawnGold(210, 210, 350, 0)
expect(engine.groundItems.count).toBe(2)
// Transition from level 1 -> level 2
engine.saveLevelGroundState(1)
engine.restoreLevelGroundState(2)
expect(engine.groundItems.count).toBe(0)
expect(engine.ground.length).toBe(0)
// Return to level 1
engine.saveLevelGroundState(2)
engine.restoreLevelGroundState(1)
expect(engine.groundItems.count).toBe(2)
expect(engine.ground.length).toBe(2)
expect(engine.ground.some(g => g.isGold && g.goldAmount === 350)).toBe(true)
expect(engine.ground.some(g => !g.isGold && g.item?.code === 'rvl')).toBe(true)
// Verify loadSnapshot also preserves non-enumerable .id getter on restored.ground entries
const drop1 = engine.ground[0]!
const snap = engine.saveSnapshot()
const restored = new GameEngine('dru')
restored.loadSnapshot(snap)
expect(restored.ground[0]!.id).toBe(drop1.id)
})
})