Exact source used by this build. SHA-256: 15e967346a06ae2373478755cc1f607cc3b10f56cede4b84f672d21cc5bd1908.
1 import json from "../../../data/community/community.json";
2 import { ITEMS, validateItemReferences } from "../items";
3 import { AREA_MAPS } from "../maps";
4 import type { ContentDomain } from "../types";
5 import { CommunityContentSchema } from "./schema";
6 export * from "./schema";
7 export const COMMUNITY_CONTENT = CommunityContentSchema.parse(json);
8 const unique = (values: readonly string[], label: string) => { if (new Set(values).size !== values.length) throw new Error(`Duplicate ${label}`); };
9 export function validateCommunityReferences(value: unknown, items = ITEMS): void {
10 const data = CommunityContentSchema.parse(value);
11 for (const key of ["rooms", "bundles", "repairs", "achievements"] as const) unique(data[key].map(entry => entry.id), key);
12 const known = new Map(items.map(item => [item.id, item]));
13 const rewards = [...data.bundles.map(bundle => bundle.reward), ...data.museum.milestones.map(milestone => milestone.reward)];
14 validateItemReferences([...data.bundles.flatMap(bundle => bundle.requirements.flatMap(requirement => requirement.itemId ? [requirement.itemId] : [])), ...rewards.flatMap(reward => reward.items.map(item => item.id)), ...data.museum.donations.map(donation => donation.itemId), ...Object.values(data.collections).flat()], items);
15 for (const reward of rewards) for (const stack of reward.items) if (stack.quality !== "regular" && !known.get(stack.id)?.qualityAllowed) throw new Error("Reward quality is not supported");
16 for (const bundle of data.bundles) {
17 if (bundle.choose > bundle.requirements.length) throw new Error("Bundle choose exceeds requirements");
18 unique(bundle.requirements.map(requirement => requirement.id), "bundle requirement");
19 for (const requirement of bundle.requirements) {
20 const matches = items.filter(item => requirement.itemId ? item.id === requirement.itemId : item.tags.includes(requirement.tag!));
21 if (!matches.length) throw new Error(`Unknown bundle tag ${requirement.tag}`);
22 if (requirement.minQuality !== "regular" && !matches.some(item => item.qualityAllowed)) throw new Error("Unobtainable bundle quality");
23 }
24 }
25 const bundleIds = data.rooms.flatMap(room => room.bundleIds);
26 unique(bundleIds, "room bundle assignment");
27 if (bundleIds.length !== data.bundles.length || bundleIds.some(id => !data.bundles.some(bundle => bundle.id === id))) throw new Error("Every bundle must belong to one room");
28 for (const room of data.rooms) for (const id of room.repairIds) if (!data.repairs.some(repair => repair.id === id)) throw new Error(`Unknown repair ${id}`);
29 const poi = (areaId: string, poiId: string) => { const area = AREA_MAPS[areaId]; if (!area || !area.pois.some(poi => poi.id === poiId)) throw new Error(`Unknown POI ${poiId}`); return area; };
30 for (const loc of [data.restoration, data.museum.location]) { const area = poi(loc.areaId, loc.poiId); const marker = area.pois.find(p => p.id === loc.poiId)!; if (loc.x < marker.x || loc.x >= marker.x + marker.width || loc.y < marker.y || loc.y >= marker.y + marker.height || area.collision[Math.floor(loc.y) * area.width + Math.floor(loc.x)]) throw new Error("Community station must be on its walkable POI"); }
31 for (const repair of data.repairs) {
32 if (!Object.values(AREA_MAPS).some(area => area.pois.some(poi => poi.id === repair.poiId))) throw new Error(`Unknown repair POI ${repair.poiId}`);
33 if (repair.kind === "warp" && !repair.warpIds.length || repair.kind === "greenhouse" && repair.warpIds.length) throw new Error("Invalid repair warp list");
34 for (const id of repair.warpIds) if (!Object.values(AREA_MAPS).some(area => area.warps.some(warp => warp.id === id && warp.locked))) throw new Error(`Unknown locked repair warp ${id}`);
35 }
36 const greenhouse = data.greenhouse, farm = poi(greenhouse.areaId, greenhouse.poiId);
37 if (greenhouse.x + greenhouse.width > farm.width || greenhouse.y + greenhouse.height > farm.height) throw new Error("Greenhouse bounds exceed area");
38 for (let y = greenhouse.y; y < greenhouse.y + greenhouse.height; y++) for (let x = greenhouse.x; x < greenhouse.x + greenhouse.width; x++) if (farm.collision[y * farm.width + x]) throw new Error("Greenhouse soil is blocked");
39 for (const areaId of data.artifactSpots.areaIds) if (!AREA_MAPS[areaId]?.outdoor) throw new Error(`Unknown outdoor artifact area ${areaId}`);
40 unique(data.museum.donations.map(entry => entry.id), "museum ID");
41 unique(data.museum.donations.map(entry => entry.itemId), "museum donation");
42 for (const donation of data.museum.donations) if (!known.get(donation.itemId)?.tags.includes(donation.kind)) throw new Error("Museum item kind must match its item tag");
43 if (data.museum.donations.length > data.museum.displaySlots || data.museum.displaySlots % data.museum.columns !== 0 || data.museum.openMinute >= data.museum.closeMinute) throw new Error("Invalid museum layout or hours");
44 let previous = 0; for (const milestone of data.museum.milestones) { if (milestone.count <= previous || milestone.count > data.museum.displaySlots) throw new Error("Museum milestones must be increasing and fit displays"); previous = milestone.count; }
45 for (const [category, ids] of Object.entries(data.collections)) unique(ids, `${category} collection`);
46 }
47 export const communityDomain: ContentDomain = { id: "community", data: COMMUNITY_CONTENT, validate() { validateCommunityReferences(COMMUNITY_CONTENT); } };
48 communityDomain.validate();
49