Files
wechat-minigame/js/content/validation/validate-city.js
2026-03-29 00:36:28 +08:00

91 lines
2.8 KiB
JavaScript

const CATEGORY_MINIMUMS = {
landmark: 2,
food: 3,
culture: 2,
item: 2,
nature: 2,
}
function countCategories(elements) {
return elements.reduce((counts, element) => {
counts[element.category] = (counts[element.category] ?? 0) + 1
return counts
}, {})
}
function isMultipleOfThree(value) {
return Number.isInteger(value) && value > 0 && value % 3 === 0
}
export function validateCity(city, continentIds, cityIds) {
const errors = []
const warnings = []
if (!city.id) {
errors.push('City is missing id')
return { errors, warnings }
}
if (!continentIds.has(city.continentId)) {
errors.push(`City "${city.id}" references unknown continent "${city.continentId}"`)
}
if (!city.display?.name || !city.display?.bgColor) {
errors.push(`City "${city.id}" is missing display metadata`)
}
if (!city.cover?.catImage || !city.cover?.catThumb) {
errors.push(`City "${city.id}" is missing cat cover assets`)
}
if (!Array.isArray(city.elements) || city.elements.length < 12 || city.elements.length > 15) {
errors.push(`City "${city.id}" must declare 12-15 elements`)
}
if (!Array.isArray(city.levelPresets) || city.levelPresets.length !== 6) {
errors.push(`City "${city.id}" must declare exactly 6 level presets`)
}
if (city.unlockAfterCityId && !cityIds.has(city.unlockAfterCityId)) {
errors.push(`City "${city.id}" unlockAfterCityId references unknown city "${city.unlockAfterCityId}"`)
}
const seenElementIds = new Set()
const seenElementNames = new Set()
const categoryCounts = countCategories(city.elements ?? [])
for (const element of city.elements ?? []) {
if (seenElementIds.has(element.id)) {
errors.push(`City "${city.id}" has duplicate element id "${element.id}"`)
}
seenElementIds.add(element.id)
if (seenElementNames.has(element.name)) {
warnings.push(`City "${city.id}" has duplicate element name "${element.name}"`)
}
seenElementNames.add(element.name)
}
for (const [category, minimum] of Object.entries(CATEGORY_MINIMUMS)) {
if ((categoryCounts[category] ?? 0) < minimum) {
errors.push(`City "${city.id}" category "${category}" must contain at least ${minimum} elements`)
}
}
for (const preset of city.levelPresets ?? []) {
const values = Array.isArray(preset.piecesPerElement) ? preset.piecesPerElement : [preset.piecesPerElement]
if (Array.isArray(preset.piecesPerElement) && preset.piecesPerElement.length !== preset.elementCount) {
errors.push(`City "${city.id}" level ${preset.id} piecesPerElement array length must match elementCount`)
}
if (!values.every(isMultipleOfThree)) {
errors.push(`City "${city.id}" level ${preset.id} piecesPerElement must contain only multiples of 3`)
}
}
return { errors, warnings }
}
export default validateCity