103 lines
3.8 KiB
JavaScript
103 lines
3.8 KiB
JavaScript
(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 };
|
|
})();
|