(function () { 'use strict'; const root = window.PixelIslandModules ||= {}; function tileKey(x, y) { return `${Math.round(x)},${Math.round(y)}`; } function pushBucket(map, key, item) { const bucket = map.get(key); if (bucket) bucket.push(item); else map.set(key, [item]); } function build(state) { const index = { assetById: new Map(), staticById: new Map(), dynamicById: new Map(), objectsById: new Map(), placedByTile: new Map(), dynamicByHomeTile: new Map(), objectIdsByAssetId: new Map() }; for (const asset of state.assets || []) { if (asset?.id) index.assetById.set(asset.id, asset); } for (const placed of state.placed || []) { if (!placed?.id) continue; index.staticById.set(placed.id, placed); index.objectsById.set(placed.id, { kind: 'static', object: placed }); pushBucket(index.placedByTile, tileKey(placed.x, placed.y), placed); pushBucket(index.objectIdsByAssetId, placed.assetId, placed.id); } for (const summon of state.dynamicSummons || []) { if (!summon?.id) continue; index.dynamicById.set(summon.id, summon); index.objectsById.set(summon.id, { kind: 'dynamic', object: summon }); pushBucket(index.dynamicByHomeTile, tileKey(summon.homeX, summon.homeY), summon); pushBucket(index.objectIdsByAssetId, summon.assetId, summon.id); } return index; } function reduce(state, event, handlers = {}) { if (!state || !event) return state; switch (event.type) { case 'asset.upsert': { const asset = handlers.unpackAsset ? handlers.unpackAsset(event.asset) : event.asset; if (!asset?.id) return state; const list = state.assets ||= []; const index = list.findIndex((item) => item.id === asset.id); if (index >= 0) list[index] = asset; else list.unshift(asset); break; } case 'asset.delete': { const assetId = event.assetId; if (!assetId) return state; const removed = new Set(); for (const item of state.placed || []) if (item.assetId === assetId) removed.add(item.id); for (const item of state.dynamicSummons || []) if (item.assetId === assetId) removed.add(item.id); state.assets = (state.assets || []).filter((asset) => asset.id !== assetId); state.placed = (state.placed || []).filter((item) => item.assetId !== assetId); state.dynamicSummons = (state.dynamicSummons || []).filter((item) => item.assetId !== assetId); delete state.assetVotes?.[assetId]; delete state.hiddenAssets?.[assetId]; for (const id of removed) { delete state.objectVotes?.[id]; delete state.hiddenObjects?.[id]; } state.moderationReports = (state.moderationReports || []).filter((report) => !removed.has(report.objectId)); break; } case 'object.upsert': { const object = handlers.unpackObject ? handlers.unpackObject(event.kind, event.object) : event.object; if (!object?.id) return state; const key = event.kind === 'dynamic' ? 'dynamicSummons' : 'placed'; const list = state[key] ||= []; const index = list.findIndex((item) => item.id === object.id); if (index >= 0) list[index] = object; else list.push(object); break; } case 'object.delete': { const key = event.kind === 'dynamic' ? 'dynamicSummons' : 'placed'; state[key] = (state[key] || []).filter((item) => item.id !== event.objectId); delete state.objectVotes?.[event.objectId]; delete state.hiddenObjects?.[event.objectId]; state.moderationReports = (state.moderationReports || []).filter((report) => report.objectId !== event.objectId); break; } } return state; } root.StateIndex = { build, reduce, tileKey }; })();