Expand Yizhou campaign release QA

This commit is contained in:
2026-07-06 18:17:09 +09:00
parent d9e2ad7ee6
commit 650e0a59a3
2 changed files with 180 additions and 15 deletions

View File

@@ -33,8 +33,8 @@
"verify:public-deploy": "node scripts/verify-public-deploy.mjs",
"qa:representative": "node scripts/qa-representative-battles.mjs",
"qa:smoke": "node scripts/qa-representative-battles.mjs --set=smoke",
"qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27",
"qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27",
"qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33",
"qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33",
"deploy:nas": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1 -Build",
"deploy:nas:dist": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1",
"ship:release": "node scripts/ship-release.mjs"

View File

@@ -221,6 +221,164 @@ const yizhouTargets = [
'yizhou-leader-yang-huai'
];
const fuPassTargets = [
'fupass-scout-a',
'fupass-scout-b',
'fupass-infantry-a',
'fupass-infantry-b',
'fupass-infantry-c',
'fupass-infantry-d',
'fupass-archer-a',
'fupass-archer-b',
'fupass-archer-c',
'fupass-cavalry-a',
'fupass-cavalry-b',
'fupass-cavalry-c',
'fupass-guard-a',
'fupass-guard-b',
'fupass-strategist-a',
'fupass-strategist-b',
'fupass-ambusher-a',
'fupass-ambusher-b',
'fupass-ambusher-c',
'fupass-leader-gao-pei'
];
const luoOuterTargets = [
'luo-scout-a',
'luo-scout-b',
'luo-infantry-a',
'luo-infantry-b',
'luo-infantry-c',
'luo-infantry-d',
'luo-archer-a',
'luo-archer-b',
'luo-archer-c',
'luo-cavalry-a',
'luo-cavalry-b',
'luo-cavalry-c',
'luo-guard-a',
'luo-guard-b',
'luo-strategist-a',
'luo-strategist-b',
'luo-ambusher-a',
'luo-ambusher-b',
'luo-officer-wu-yi',
'luo-leader-zhang-ren'
];
const luofengTargets = [
'luofeng-scout-a',
'luofeng-scout-b',
'luofeng-scout-c',
'luofeng-infantry-a',
'luofeng-infantry-b',
'luofeng-infantry-c',
'luofeng-infantry-d',
'luofeng-archer-a',
'luofeng-archer-b',
'luofeng-archer-c',
'luofeng-archer-d',
'luofeng-cavalry-a',
'luofeng-cavalry-b',
'luofeng-cavalry-c',
'luofeng-guard-a',
'luofeng-guard-b',
'luofeng-strategist-a',
'luofeng-strategist-b',
'luofeng-ambusher-a',
'luofeng-ambusher-b',
'luofeng-ambusher-c',
'luofeng-ambusher-d',
'luofeng-leader-zhang-ren'
];
const luoMainTargets = [
'luo-main-scout-a',
'luo-main-scout-b',
'luo-main-infantry-a',
'luo-main-infantry-b',
'luo-main-infantry-c',
'luo-main-infantry-d',
'luo-main-infantry-e',
'luo-main-archer-a',
'luo-main-archer-b',
'luo-main-archer-c',
'luo-main-archer-d',
'luo-main-cavalry-a',
'luo-main-cavalry-b',
'luo-main-cavalry-c',
'luo-main-guard-a',
'luo-main-guard-b',
'luo-main-guard-c',
'luo-main-strategist-a',
'luo-main-strategist-b',
'luo-main-ambusher-a',
'luo-main-ambusher-b',
'luo-main-ambusher-c',
'luo-main-veteran-yan-yan',
'luo-main-officer-a',
'luo-main-leader-zhang-ren'
];
const mianzhuTargets = [
'chengdu-scout-a',
'chengdu-scout-b',
'chengdu-scout-c',
'chengdu-infantry-a',
'chengdu-infantry-b',
'chengdu-infantry-c',
'chengdu-infantry-d',
'chengdu-infantry-e',
'chengdu-infantry-f',
'chengdu-archer-a',
'chengdu-archer-b',
'chengdu-archer-c',
'chengdu-archer-d',
'chengdu-cavalry-a',
'chengdu-cavalry-b',
'chengdu-cavalry-c',
'chengdu-guard-a',
'chengdu-guard-b',
'chengdu-guard-c',
'chengdu-strategist-a',
'chengdu-strategist-b',
'chengdu-ambusher-a',
'chengdu-ambusher-b',
'chengdu-ambusher-c',
'chengdu-officer-fei-guan',
'chengdu-leader-li-yan'
];
const chengduFinalTargets = [
'chengdu-final-scout-a',
'chengdu-final-scout-b',
'chengdu-final-scout-c',
'chengdu-final-infantry-a',
'chengdu-final-infantry-b',
'chengdu-final-infantry-c',
'chengdu-final-infantry-d',
'chengdu-final-infantry-e',
'chengdu-final-infantry-f',
'chengdu-final-archer-a',
'chengdu-final-archer-b',
'chengdu-final-archer-c',
'chengdu-final-archer-d',
'chengdu-final-cavalry-a',
'chengdu-final-cavalry-b',
'chengdu-final-cavalry-c',
'chengdu-final-guard-a',
'chengdu-final-guard-b',
'chengdu-final-guard-c',
'chengdu-final-strategist-a',
'chengdu-final-strategist-b',
'chengdu-final-ambusher-a',
'chengdu-final-ambusher-b',
'chengdu-final-ambusher-c',
'chengdu-final-officer-a',
'chengdu-final-leader-huang-quan'
];
const earlyCampaignBattles = [
{ no: 1, id: 'first-battle-zhuo-commandery', selected: [], targets: ['rebel-archer-b', 'rebel-leader'], protected: ['liu-bei'] },
{
@@ -566,14 +724,14 @@ const southernCampaignBattles = [
no: 28,
id: 'twenty-eighth-battle-fu-pass-entry',
selected: fuPassSortie,
targets: ['fupass-leader-gao-pei'],
targets: fuPassTargets,
protected: ['liu-bei', 'zhuge-liang']
},
{
no: 29,
id: 'twenty-ninth-battle-luo-outer-wall',
selected: luoOuterSortie,
targets: ['luo-leader-zhang-ren', 'luo-archer-b', 'luo-officer-wu-yi', 'luo-strategist-a', 'luo-guard-a', 'luo-guard-b'],
targets: luoOuterTargets,
protected: ['liu-bei', 'zhuge-liang']
},
{
@@ -590,21 +748,21 @@ const hanzhongCampaignBattles = [
no: 31,
id: 'thirty-first-battle-luo-main-gate',
selected: luoMainSortie,
targets: ['luo-main-leader-zhang-ren'],
targets: luoMainTargets,
protected: ['liu-bei', 'pang-tong']
},
{
no: 32,
id: 'thirty-second-battle-mianzhu-gate',
selected: mianzhuSortie,
targets: ['chengdu-leader-li-yan'],
targets: mianzhuTargets,
protected: ['liu-bei', 'yan-yan']
},
{
no: 33,
id: 'thirty-third-battle-chengdu-surrender',
selected: chengduSortie,
targets: ['chengdu-final-leader-huang-quan'],
targets: chengduFinalTargets,
protected: ['liu-bei', 'li-yan']
},
{
@@ -1273,9 +1431,9 @@ async function normalizeRepresentativeBattleUnits(page, battle) {
}
const selectedIds = new Set([...(selected ?? []), ...(protectedUnitIds ?? [])]);
const hpFloor = no >= 28 ? 86 : no >= 21 ? 84 : no >= 18 ? 84 : no >= 14 ? 80 : no >= 11 ? 68 : no >= 2 ? 56 : 0;
const attackFloor = no >= 28 ? 78 : no >= 21 ? 70 : no >= 18 ? 64 : no >= 14 ? 60 : no >= 11 ? 42 : no >= 2 ? 24 : 0;
const statFloor = no >= 28 ? 132 : no >= 21 ? 126 : no >= 18 ? 122 : no >= 14 ? 118 : no >= 11 ? 98 : no >= 2 ? 88 : 0;
const hpFloor = no >= 30 ? 520 : no >= 28 ? 360 : no >= 21 ? 84 : no >= 18 ? 84 : no >= 14 ? 80 : no >= 11 ? 68 : no >= 2 ? 56 : 0;
const attackFloor = no >= 30 ? 126 : no >= 28 ? 96 : no >= 21 ? 70 : no >= 18 ? 64 : no >= 14 ? 60 : no >= 11 ? 42 : no >= 2 ? 24 : 0;
const statFloor = no >= 30 ? 180 : no >= 28 ? 150 : no >= 21 ? 126 : no >= 18 ? 122 : no >= 14 ? 118 : no >= 11 ? 98 : no >= 2 ? 88 : 0;
if (hpFloor <= 0) {
return;
@@ -1634,10 +1792,11 @@ async function playBattleWithoutDebugVictory(page, battle) {
}
const pressProtected = shouldPressProtected(unit);
const isTarget = targetIds.has(attack.target.id);
const isLeaderTarget = attack.target.id.includes('leader');
const kill = attack.preview.damage >= attack.target.hp;
if (isLastProtectedFighter(unit)) {
const hpRate = unit.hp / Math.max(1, unit.maxHp);
if (isTarget && hpRate >= 0.45) {
if ((isLeaderTarget || isTarget) && hpRate >= 0.45) {
return true;
}
if (kill && hpRate >= 0.35) {
@@ -1645,16 +1804,16 @@ async function playBattleWithoutDebugVictory(page, battle) {
}
return threat <= Math.floor(unit.hp * 1.05);
}
if (protectedIds.size > 1 && !isTarget && unit.id === 'sun-qian') {
if (protectedIds.size > 1 && !isLeaderTarget && unit.id === 'sun-qian') {
return kill && liveUnits('enemy').length <= 3 && threat <= Math.floor(unit.hp * 0.12);
}
if (protectedIds.size > 1 && !isTarget) {
if (protectedIds.size > 1 && !isLeaderTarget) {
return unit.hp >= Math.ceil(unit.maxHp * 0.75) && threat <= Math.floor(unit.hp * 0.22);
}
if (isTarget && (pressProtected || kill) && threat <= Math.floor(unit.hp * (pressProtected ? 0.7 : 0.42))) {
if (isLeaderTarget && (pressProtected || kill) && threat <= Math.floor(unit.hp * (pressProtected ? 0.7 : 0.42))) {
return true;
}
if (isTarget && protectedIds.size === 1 && threat <= Math.floor(unit.hp * 0.3)) {
if (isLeaderTarget && protectedIds.size === 1 && threat <= Math.floor(unit.hp * 0.3)) {
return true;
}
if (kill && threat <= Math.floor(unit.hp * (pressProtected ? 0.5 : 0.32))) {
@@ -1723,6 +1882,12 @@ async function playBattleWithoutDebugVictory(page, battle) {
return { x: 25, y: 13 };
}
}
if (no === 29) {
const routeDone = (state().objectives ?? []).find((objective) => objective.id === 'wu-yi-route')?.achieved;
if (!routeDone) {
return { x: 47, y: 23 };
}
}
return undefined;
}