241 lines
8.7 KiB
JavaScript
241 lines
8.7 KiB
JavaScript
import { createServer } from 'vite';
|
|
|
|
const server = await createServer({
|
|
logLevel: 'error',
|
|
server: { middlewareMode: true, hmr: false },
|
|
appType: 'custom'
|
|
});
|
|
|
|
try {
|
|
const {
|
|
getUnitSignatureStrategy,
|
|
getUnitStrategies,
|
|
getUnitStrategyCoverage,
|
|
strategyCoverageDefinitions,
|
|
unitStrategyIds,
|
|
usableCatalog
|
|
} = await server.ssrLoadModule('/src/game/data/battleUsables.ts');
|
|
|
|
const signatures = [
|
|
{
|
|
ownerId: 'liu-bei',
|
|
usableId: 'benevolentCommand',
|
|
name: '인덕의 호령',
|
|
target: 'ally',
|
|
effect: 'focus',
|
|
range: 3,
|
|
power: 0,
|
|
attackBonus: 2,
|
|
hitBonus: 10,
|
|
duration: 2,
|
|
accuracyBonus: 0,
|
|
criticalBonus: 4,
|
|
statusEffect: null,
|
|
statusDuration: 0,
|
|
statusPower: 0,
|
|
coverageIds: ['buff'],
|
|
expectedStrategies: ['aid', 'benevolentCommand']
|
|
},
|
|
{
|
|
ownerId: 'guan-yu',
|
|
usableId: 'azureDragonStrike',
|
|
name: '청룡일섬',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 1,
|
|
power: 13,
|
|
accuracyBonus: 6,
|
|
criticalBonus: 10,
|
|
statusEffect: null,
|
|
statusDuration: 0,
|
|
statusPower: 0,
|
|
coverageIds: ['offense'],
|
|
expectedStrategies: ['azureDragonStrike']
|
|
},
|
|
{
|
|
ownerId: 'zhang-fei',
|
|
usableId: 'changbanRoar',
|
|
name: '장판교 대갈',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 2,
|
|
power: 8,
|
|
accuracyBonus: 12,
|
|
criticalBonus: 0,
|
|
statusEffect: 'confusion',
|
|
statusDuration: 1,
|
|
statusPower: 1,
|
|
coverageIds: ['offense', 'control'],
|
|
expectedStrategies: ['changbanRoar']
|
|
},
|
|
{
|
|
ownerId: 'zhao-yun',
|
|
usableId: 'singleRiderRescue',
|
|
name: '단기구원',
|
|
target: 'ally',
|
|
effect: 'heal',
|
|
range: 3,
|
|
power: 12,
|
|
accuracyBonus: 0,
|
|
criticalBonus: 0,
|
|
statusEffect: null,
|
|
statusDuration: 0,
|
|
statusPower: 0,
|
|
coverageIds: ['healing'],
|
|
expectedStrategies: ['singleRiderRescue']
|
|
},
|
|
{
|
|
ownerId: 'zhuge-liang',
|
|
usableId: 'eastWindFire',
|
|
name: '동남풍 화계',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 3,
|
|
power: 11,
|
|
accuracyBonus: 6,
|
|
criticalBonus: 0,
|
|
statusEffect: 'burn',
|
|
statusDuration: 3,
|
|
statusPower: 3,
|
|
coverageIds: ['offense'],
|
|
expectedStrategies: ['aid', 'encourage', 'eastWindFire']
|
|
},
|
|
{
|
|
ownerId: 'huang-zhong',
|
|
usableId: 'hundredPacePierce',
|
|
name: '백보천양',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 4,
|
|
power: 11,
|
|
accuracyBonus: 18,
|
|
criticalBonus: 12,
|
|
statusEffect: null,
|
|
statusDuration: 0,
|
|
statusPower: 0,
|
|
coverageIds: ['offense'],
|
|
expectedStrategies: ['hundredPacePierce']
|
|
},
|
|
{
|
|
ownerId: 'ma-chao',
|
|
usableId: 'westernCavalryCharge',
|
|
name: '서량철기',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 2,
|
|
power: 11,
|
|
accuracyBonus: 8,
|
|
criticalBonus: 6,
|
|
statusEffect: 'confusion',
|
|
statusDuration: 1,
|
|
statusPower: 1,
|
|
coverageIds: ['offense', 'control'],
|
|
expectedStrategies: ['westernCavalryCharge', 'encourage']
|
|
},
|
|
{
|
|
ownerId: 'jiang-wei',
|
|
usableId: 'qilinStratagem',
|
|
name: '기린지계',
|
|
target: 'enemy',
|
|
effect: 'damage',
|
|
range: 3,
|
|
power: 10,
|
|
accuracyBonus: 10,
|
|
criticalBonus: 2,
|
|
statusEffect: 'confusion',
|
|
statusDuration: 2,
|
|
statusPower: 1,
|
|
coverageIds: ['offense', 'control'],
|
|
expectedStrategies: ['aid', 'encourage', 'qilinStratagem']
|
|
}
|
|
];
|
|
|
|
assert(
|
|
JSON.stringify(strategyCoverageDefinitions.map(({ id, label }) => ({ id, label }))) === JSON.stringify([
|
|
{ id: 'healing', label: '회복' },
|
|
{ id: 'buff', label: '강화' },
|
|
{ id: 'offense', label: '공격' },
|
|
{ id: 'control', label: '제어' }
|
|
]),
|
|
`Unexpected strategy coverage contract: ${JSON.stringify(strategyCoverageDefinitions)}`
|
|
);
|
|
|
|
const signatureNames = new Set();
|
|
signatures.forEach((expected) => {
|
|
const usable = usableCatalog[expected.usableId];
|
|
assert(usable, `Missing signature strategy ${expected.usableId}.`);
|
|
assert(usable.id === expected.usableId, `Catalog key/id mismatch for ${expected.usableId}: ${usable.id}`);
|
|
assert(usable.command === 'strategy', `Signature strategy must use the strategy command: ${expected.usableId}`);
|
|
assert(usable.signatureOwnerId === expected.ownerId, `Unexpected signature owner for ${expected.usableId}: ${usable.signatureOwnerId}`);
|
|
assert(usable.name === expected.name, `Unexpected signature name for ${expected.usableId}: ${usable.name}`);
|
|
assert(usable.target === expected.target && usable.effect === expected.effect, `Unexpected target/effect for ${expected.usableId}.`);
|
|
assert(usable.range === expected.range && usable.power === expected.power, `Unexpected range/power for ${expected.usableId}.`);
|
|
assert((usable.attackBonus ?? 0) === (expected.attackBonus ?? 0), `Unexpected attack bonus for ${expected.usableId}.`);
|
|
assert((usable.hitBonus ?? 0) === (expected.hitBonus ?? 0), `Unexpected hit bonus for ${expected.usableId}.`);
|
|
assert((usable.duration ?? 0) === (expected.duration ?? 0), `Unexpected effect duration for ${expected.usableId}.`);
|
|
assert((usable.accuracyBonus ?? 0) === expected.accuracyBonus, `Unexpected accuracy bonus for ${expected.usableId}.`);
|
|
assert((usable.criticalBonus ?? 0) === expected.criticalBonus, `Unexpected critical bonus for ${expected.usableId}.`);
|
|
assert((usable.statusEffect ?? null) === expected.statusEffect, `Unexpected status for ${expected.usableId}.`);
|
|
assert((usable.statusDuration ?? 0) === expected.statusDuration, `Unexpected status duration for ${expected.usableId}.`);
|
|
assert((usable.statusPower ?? 0) === expected.statusPower, `Unexpected status power for ${expected.usableId}.`);
|
|
assert(JSON.stringify(usable.coverageIds ?? []) === JSON.stringify(expected.coverageIds), `Unexpected coverage for ${expected.usableId}.`);
|
|
assert(typeof usable.description === 'string' && usable.description.trim().length >= 20, `Signature strategy needs a meaningful description: ${expected.usableId}`);
|
|
assert(!signatureNames.has(usable.name), `Duplicate signature strategy name: ${usable.name}`);
|
|
signatureNames.add(usable.name);
|
|
|
|
assert(
|
|
JSON.stringify(unitStrategyIds[expected.ownerId]) === JSON.stringify(expected.expectedStrategies),
|
|
`Unexpected strategy loadout for ${expected.ownerId}: ${JSON.stringify(unitStrategyIds[expected.ownerId])}`
|
|
);
|
|
assert(
|
|
getUnitSignatureStrategy(expected.ownerId)?.id === expected.usableId,
|
|
`Signature lookup did not resolve ${expected.ownerId} -> ${expected.usableId}.`
|
|
);
|
|
assert(
|
|
getUnitStrategies([expected.ownerId]).filter((candidate) => candidate.id === expected.usableId).length === 1,
|
|
`Signature strategy must appear exactly once for ${expected.ownerId}.`
|
|
);
|
|
|
|
Object.entries(unitStrategyIds)
|
|
.filter(([unitId]) => unitId !== expected.ownerId)
|
|
.forEach(([unitId, strategyIds]) => {
|
|
assert(!strategyIds.includes(expected.usableId), `${expected.usableId} leaked into ${unitId}'s strategy loadout.`);
|
|
});
|
|
});
|
|
|
|
const assignedStrategyIds = Object.values(unitStrategyIds).flat();
|
|
assignedStrategyIds.forEach((strategyId) => {
|
|
assert(usableCatalog[strategyId]?.command === 'strategy', `Assigned strategy does not resolve to a strategy usable: ${strategyId}`);
|
|
});
|
|
assert(
|
|
Object.entries(usableCatalog)
|
|
.filter(([, usable]) => usable.signatureOwnerId)
|
|
.every(([usableId, usable]) => signatures.some((entry) => entry.usableId === usableId && entry.ownerId === usable.signatureOwnerId)),
|
|
'Every signature strategy must be covered by the eight-officer contract.'
|
|
);
|
|
|
|
const brotherCoverage = getUnitStrategyCoverage(['liu-bei', 'guan-yu', 'zhang-fei', 'liu-bei']);
|
|
assert(
|
|
brotherCoverage.count === 4 && brotherCoverage.total === 4 && brotherCoverage.missing.length === 0,
|
|
`The founding trio must retain full strategy coverage: ${JSON.stringify(brotherCoverage)}`
|
|
);
|
|
assert(
|
|
getUnitStrategyCoverage(['guan-yu', 'huang-zhong']).count === 1,
|
|
'Duplicate offense signatures must not inflate coverage.'
|
|
);
|
|
assert(
|
|
JSON.stringify(getUnitStrategyCoverage(['zhang-fei']).covered.map((definition) => definition.id)) === JSON.stringify(['offense', 'control']),
|
|
`Zhang Fei's signature must expose offense and control coverage: ${JSON.stringify(getUnitStrategyCoverage(['zhang-fei']))}`
|
|
);
|
|
|
|
console.log('Verified eight owner-exclusive signature strategies, their combat metadata, loadouts, and strategy coverage.');
|
|
} finally {
|
|
await server.close();
|
|
}
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message);
|
|
}
|
|
}
|