feat: improve portraits and combat audio
This commit is contained in:
@@ -25,7 +25,13 @@ try {
|
||||
storyBackgroundKeysForPages,
|
||||
storyBackgroundPageHasFixedAssignment
|
||||
} = await server.ssrLoadModule('/src/game/data/storyAssets.ts');
|
||||
const { portraitAssetEntriesForKey, portraitKeyForSpeaker } = await server.ssrLoadModule('/src/game/data/portraitAssets.ts');
|
||||
const {
|
||||
portraitAssets,
|
||||
portraitAssetEntriesForKey,
|
||||
portraitAssetEntriesForStoryContext,
|
||||
portraitTextureKeysForKey,
|
||||
portraitKeyForSpeaker
|
||||
} = await server.ssrLoadModule('/src/game/data/portraitAssets.ts');
|
||||
const { storyCutsceneActorProfileFor } = await server.ssrLoadModule('/src/game/data/storyCutscenes.ts');
|
||||
const { hasUnitSheetAsset } = await server.ssrLoadModule('/src/game/data/unitAssets.ts');
|
||||
|
||||
@@ -46,6 +52,14 @@ try {
|
||||
let pageCount = 0;
|
||||
let cutsceneCount = 0;
|
||||
|
||||
validatePortraitVersionSelection({
|
||||
errors,
|
||||
portraitAssets,
|
||||
portraitAssetEntriesForKey,
|
||||
portraitAssetEntriesForStoryContext,
|
||||
portraitTextureKeysForKey
|
||||
});
|
||||
|
||||
storySources.forEach(({ source, pages }) => {
|
||||
if (!Array.isArray(pages) || pages.length === 0) {
|
||||
errors.push(`${source}: story page list must not be empty`);
|
||||
@@ -86,6 +100,7 @@ try {
|
||||
storyBackgroundAssets,
|
||||
storyBackgroundKeysFor,
|
||||
portraitAssetEntriesForKey,
|
||||
portraitAssetEntriesForStoryContext,
|
||||
portraitKeyForSpeaker,
|
||||
storyCutsceneActorProfileFor,
|
||||
hasUnitSheetAsset
|
||||
@@ -124,6 +139,152 @@ try {
|
||||
await server.close();
|
||||
}
|
||||
|
||||
function validatePortraitVersionSelection(context) {
|
||||
const {
|
||||
errors,
|
||||
portraitAssets,
|
||||
portraitAssetEntriesForKey,
|
||||
portraitAssetEntriesForStoryContext,
|
||||
portraitTextureKeysForKey
|
||||
} = context;
|
||||
const zhugeLiangTextureKeys = portraitTextureKeysForKey('zhugeLiang');
|
||||
const requiredZhugeLiangTextureKeys = [
|
||||
'portrait-zhuge-liang',
|
||||
'portrait-zhuge-liang-campaign-v2',
|
||||
'portrait-zhuge-liang-northern-v2',
|
||||
'portrait-zhuge-liang-redcliffs-v2'
|
||||
];
|
||||
const supersededZhugeLiangTextureKeys = [
|
||||
'portrait-zhuge-liang-campaign',
|
||||
'portrait-zhuge-liang-northern',
|
||||
'portrait-zhuge-liang-redcliffs'
|
||||
];
|
||||
|
||||
requiredZhugeLiangTextureKeys.forEach((textureKey) => {
|
||||
if (!zhugeLiangTextureKeys.includes(textureKey)) {
|
||||
errors.push(`portrait version selection: missing current Zhuge Liang portrait "${textureKey}"`);
|
||||
}
|
||||
});
|
||||
supersededZhugeLiangTextureKeys.forEach((textureKey) => {
|
||||
if (zhugeLiangTextureKeys.includes(textureKey)) {
|
||||
errors.push(`portrait version selection: superseded Zhuge Liang portrait remains selectable "${textureKey}"`);
|
||||
}
|
||||
});
|
||||
|
||||
const zhugeLiangStoryCases = [
|
||||
{
|
||||
context: { id: 'seventeenth-zhuge-liang-counsel', background: 'story-wolong' },
|
||||
expected: 'portrait-zhuge-liang'
|
||||
},
|
||||
{
|
||||
context: { id: 'eighteenth-red-cliffs-wind', background: 'story-wolong' },
|
||||
expected: 'portrait-zhuge-liang-redcliffs-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'twenty-first-zhuge-wind', background: 'story-red-cliffs' },
|
||||
expected: 'portrait-zhuge-liang-redcliffs-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'fiftieth-third-capture-plan', background: 'story-nanzhong-captures' },
|
||||
expected: 'portrait-zhuge-liang-campaign-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'thirty-fifth-hanzhong-front', background: 'story-northern' },
|
||||
expected: 'portrait-zhuge-liang-campaign-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'northern-prep-back-secured', background: 'story-northern' },
|
||||
expected: 'portrait-zhuge-liang-northern-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'fifty-fifth-qishan-road-opens', background: 'story-northern' },
|
||||
expected: 'portrait-zhuge-liang-northern-v2'
|
||||
},
|
||||
{
|
||||
context: { id: 'sixtieth-wudu-yinping-order', background: 'story-wudu-yinping' },
|
||||
expected: 'portrait-zhuge-liang-northern-v2'
|
||||
}
|
||||
];
|
||||
|
||||
zhugeLiangStoryCases.forEach(({ context: storyContext, expected }) => {
|
||||
const selected = portraitAssetEntriesForStoryContext('zhugeLiang', storyContext).map(
|
||||
({ textureKey }) => textureKey
|
||||
);
|
||||
if (selected.length !== 1 || selected[0] !== expected) {
|
||||
errors.push(
|
||||
`portrait story context: ${storyContext.id} should select "${expected}", received [${selected.join(', ')}]`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const zhugeLiangBaseRevisionFixture = 'zhuge-liang-v2';
|
||||
const previousBaseRevision = portraitAssets[zhugeLiangBaseRevisionFixture];
|
||||
portraitAssets[zhugeLiangBaseRevisionFixture] = `virtual:${zhugeLiangBaseRevisionFixture}`;
|
||||
try {
|
||||
const selected = portraitAssetEntriesForStoryContext('zhugeLiang', {
|
||||
id: 'seventeenth-zhuge-liang-counsel',
|
||||
background: 'story-wolong'
|
||||
}).map(({ textureKey }) => textureKey);
|
||||
if (selected.length !== 1 || selected[0] !== 'portrait-zhuge-liang-v2') {
|
||||
errors.push(
|
||||
`portrait story context: a revised base portrait should remain in the base route, received [${selected.join(', ')}]`
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (previousBaseRevision === undefined) {
|
||||
delete portraitAssets[zhugeLiangBaseRevisionFixture];
|
||||
} else {
|
||||
portraitAssets[zhugeLiangBaseRevisionFixture] = previousBaseRevision;
|
||||
}
|
||||
}
|
||||
|
||||
const fixtureSlug = 'portrait-version-fixture';
|
||||
const fixtureSlugs = [
|
||||
fixtureSlug,
|
||||
`${fixtureSlug}-campaign`,
|
||||
`${fixtureSlug}-campaign-v2`,
|
||||
`${fixtureSlug}-campaign-v10`,
|
||||
`${fixtureSlug}-northern`,
|
||||
`${fixtureSlug}-redcliffs-v2`,
|
||||
`${fixtureSlug}-redcliffs-v3`,
|
||||
`${fixtureSlug}-vanguard`,
|
||||
`${fixtureSlug}-veteran`
|
||||
];
|
||||
const expectedFixtureTextureKeys = [
|
||||
`portrait-${fixtureSlug}`,
|
||||
`portrait-${fixtureSlug}-campaign-v10`,
|
||||
`portrait-${fixtureSlug}-northern`,
|
||||
`portrait-${fixtureSlug}-redcliffs-v3`,
|
||||
`portrait-${fixtureSlug}-vanguard`,
|
||||
`portrait-${fixtureSlug}-veteran`
|
||||
];
|
||||
|
||||
fixtureSlugs.forEach((slug) => {
|
||||
portraitAssets[slug] = `virtual:${slug}`;
|
||||
});
|
||||
|
||||
try {
|
||||
const entryTextureKeys = portraitAssetEntriesForKey(fixtureSlug).map((entry) => entry.textureKey);
|
||||
const textureKeys = portraitTextureKeysForKey(fixtureSlug);
|
||||
if (entryTextureKeys.join('\n') !== expectedFixtureTextureKeys.join('\n')) {
|
||||
errors.push(
|
||||
`portrait version selection: entries should keep independent variants and only the highest numeric -vN suffix; ` +
|
||||
`received [${entryTextureKeys.join(', ')}]`
|
||||
);
|
||||
}
|
||||
if (textureKeys.join('\n') !== expectedFixtureTextureKeys.join('\n')) {
|
||||
errors.push(
|
||||
`portrait version selection: texture keys should keep independent variants and only the highest numeric -vN suffix; ` +
|
||||
`received [${textureKeys.join(', ')}]`
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
fixtureSlugs.forEach((slug) => {
|
||||
delete portraitAssets[slug];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function collectStorySources(scenarioModule, battleScenarios) {
|
||||
const sources = [];
|
||||
const seenArrays = new WeakSet();
|
||||
@@ -350,13 +511,13 @@ function validateRepeatedStoryBackgroundDistribution(errors, selectedBackgroundU
|
||||
}
|
||||
|
||||
function validatePortrait(context, pageContext) {
|
||||
const { errors, page, usedPortraitKeys, portraitAssetEntriesForKey, portraitKeyForSpeaker } = context;
|
||||
const { errors, page, usedPortraitKeys, portraitAssetEntriesForStoryContext, portraitKeyForSpeaker } = context;
|
||||
const portraitKey = page.portrait ?? portraitKeyForSpeaker(page.speaker);
|
||||
if (!portraitKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = portraitAssetEntriesForKey(portraitKey);
|
||||
const entries = portraitAssetEntriesForStoryContext(portraitKey, page);
|
||||
if (entries.length === 0) {
|
||||
errors.push(`${pageContext}: missing portrait asset "${portraitKey}"`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user