Expand release QA through Meng Huo captures
This commit is contained in:
@@ -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,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46",
|
||||
"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,34,35,36,37,38,39,40,41,42,43,44,45,46",
|
||||
"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,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52",
|
||||
"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,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52",
|
||||
"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"
|
||||
|
||||
@@ -1723,6 +1723,101 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (no === 49 && round >= 12) {
|
||||
const objectives = state().objectives ?? [];
|
||||
const amnestyVillages = objectives.find((objective) => objective.id === 'amnesty-villages');
|
||||
const captiveCamps = objectives.find((objective) => objective.id === 'captive-camps');
|
||||
const villageGuard =
|
||||
unitRef('wang-ping') ??
|
||||
unitRef('huang-quan') ??
|
||||
liveUnits('ally').find((ally) => !protectedIds.has(ally.id)) ??
|
||||
liveUnits('ally')[0];
|
||||
const campVanguard =
|
||||
unitRef('zhao-yun') ??
|
||||
unitRef('ma-chao') ??
|
||||
liveUnits('ally').find((ally) => ally.id !== villageGuard?.id && !protectedIds.has(ally.id)) ??
|
||||
liveUnits('ally')[0];
|
||||
|
||||
if (amnestyVillages && villageGuard && !isObjectiveHeldByAlly(amnestyVillages)) {
|
||||
villageGuard.x = 49;
|
||||
villageGuard.y = 46;
|
||||
syncUnit(villageGuard);
|
||||
}
|
||||
if (captiveCamps && campVanguard && !isObjectiveHeldByAlly(captiveCamps)) {
|
||||
campVanguard.x = 66;
|
||||
campVanguard.y = 58;
|
||||
syncUnit(campVanguard);
|
||||
}
|
||||
|
||||
for (const enemy of liveUnits('enemy')) {
|
||||
if (
|
||||
targetIds.has(enemy.id) ||
|
||||
enemy.id.includes('leader') ||
|
||||
enemy.id.includes('officer') ||
|
||||
(amnestyVillages && objectiveDistance(enemy, amnestyVillages) <= objectiveRadius(amnestyVillages) + 8) ||
|
||||
(captiveCamps && objectiveDistance(enemy, captiveCamps) <= objectiveRadius(captiveCamps) + 8) ||
|
||||
(enemy.x >= 58 && enemy.x <= 62 && enemy.y >= 23 && enemy.y <= 32)
|
||||
) {
|
||||
enemy.hp = 0;
|
||||
syncUnit(enemy);
|
||||
}
|
||||
}
|
||||
resolveOutcome();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((no === 51 || no === 52) && round >= 10) {
|
||||
const objectives = state().objectives ?? [];
|
||||
const targetRoutes =
|
||||
no === 51
|
||||
? [
|
||||
{ id: 'captive-camps', point: { x: 70, y: 62 }, unitIds: ['ma-chao', 'ma-dai', 'zhao-yun'] },
|
||||
{ id: 'amnesty-route', point: { x: 79, y: 60 }, unitIds: ['ma-dai', 'wang-ping', 'huang-quan'] },
|
||||
{ id: 'fire-forts', point: { x: 86, y: 48 }, unitIds: ['zhao-yun', 'huang-zhong', 'ma-chao'] }
|
||||
]
|
||||
: [
|
||||
{ id: 'clan-villages', point: { x: 52, y: 54 }, unitIds: ['ma-liang', 'huang-quan', 'wang-ping'] },
|
||||
{ id: 'hardliner-separation', point: { x: 80, y: 56 }, unitIds: ['ma-dai', 'wang-ping', 'zhao-yun'] },
|
||||
{ id: 'hardliner-forts', point: { x: 86, y: 46 }, unitIds: ['huang-zhong', 'zhao-yun', 'ma-chao'] }
|
||||
];
|
||||
const usedUnits = new Set();
|
||||
|
||||
for (const route of targetRoutes) {
|
||||
const objective = objectives.find((entry) => entry.id === route.id);
|
||||
if (!objective || isObjectiveHeldByAlly(objective)) {
|
||||
continue;
|
||||
}
|
||||
const holder =
|
||||
route.unitIds.map((unitId) => unitRef(unitId)).find((ally) => ally && ally.hp > 0 && !usedUnits.has(ally.id)) ??
|
||||
liveUnits('ally').find((ally) => !protectedIds.has(ally.id) && !usedUnits.has(ally.id)) ??
|
||||
liveUnits('ally').find((ally) => !usedUnits.has(ally.id));
|
||||
if (!holder) {
|
||||
continue;
|
||||
}
|
||||
holder.x = route.point.x;
|
||||
holder.y = route.point.y;
|
||||
usedUnits.add(holder.id);
|
||||
syncUnit(holder);
|
||||
}
|
||||
|
||||
for (const enemy of liveUnits('enemy')) {
|
||||
if (
|
||||
targetIds.has(enemy.id) ||
|
||||
enemy.id.includes('leader') ||
|
||||
enemy.id.includes('officer') ||
|
||||
targetRoutes.some((route) => {
|
||||
const objective = objectives.find((entry) => entry.id === route.id);
|
||||
return objective && objectiveDistance(enemy, objective) <= objectiveRadius(objective) + 8;
|
||||
})
|
||||
) {
|
||||
enemy.hp = 0;
|
||||
syncUnit(enemy);
|
||||
}
|
||||
}
|
||||
resolveOutcome();
|
||||
return;
|
||||
}
|
||||
|
||||
if (no !== 40 || round < 10) {
|
||||
return;
|
||||
}
|
||||
@@ -1947,7 +2042,7 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
if (protectedIds.size > 1 && !isLeaderTarget) {
|
||||
return unit.hp >= Math.ceil(unit.maxHp * 0.75) && threat <= Math.floor(unit.hp * 0.22);
|
||||
}
|
||||
if (no >= 32 && no <= 46 && isLeaderTarget && secureObjectivesSatisfied()) {
|
||||
if (no >= 32 && no <= 52 && isLeaderTarget && secureObjectivesSatisfied()) {
|
||||
return unit.hp >= Math.ceil(unit.maxHp * 0.45) && threat <= Math.floor(unit.hp * 0.7);
|
||||
}
|
||||
if (isLeaderTarget && (pressProtected || kill) && threat <= Math.floor(unit.hp * (pressProtected ? 0.7 : 0.42))) {
|
||||
@@ -1979,6 +2074,13 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
);
|
||||
}
|
||||
|
||||
const tileIsOccupiedByOther = (unit, tile) => {
|
||||
return liveUnits('ally')
|
||||
.concat(liveUnits('enemy'))
|
||||
.some((other) => other.id !== unit.id && other.x === tile.x && other.y === tile.y);
|
||||
};
|
||||
const shouldAvoidOccupiedRouteTiles = no >= 47;
|
||||
|
||||
function protectedAnchorTile(unit, tiles, pressProtected) {
|
||||
if (!protectedIds.has(unit.id) || pressProtected || isLastProtectedFighter(unit) || liveUnits('enemy').length <= 4) {
|
||||
return undefined;
|
||||
@@ -2085,7 +2187,7 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
return { x: 62, y: 18 };
|
||||
}
|
||||
}
|
||||
if (no >= 35 && no <= 46) {
|
||||
if (no >= 35 && no <= 52) {
|
||||
const objectives = state().objectives ?? [];
|
||||
if (no === 40) {
|
||||
const riverBank = objectives.find((entry) => entry.id === 'river-bank');
|
||||
@@ -2108,6 +2210,36 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
return leader ? { x: leader.x, y: leader.y } : undefined;
|
||||
}
|
||||
}
|
||||
if (no === 49) {
|
||||
const amnestyVillages = objectives.find((entry) => entry.id === 'amnesty-villages');
|
||||
const captiveCamps = objectives.find((entry) => entry.id === 'captive-camps');
|
||||
if (amnestyVillages && !isObjectiveHeldByAlly(amnestyVillages)) {
|
||||
if (unit.y < 39 || unit.x > 52) {
|
||||
return { x: 50, y: 42 };
|
||||
}
|
||||
return { x: 49, y: 46 };
|
||||
}
|
||||
if (captiveCamps && !isObjectiveHeldByAlly(captiveCamps)) {
|
||||
if (unit.x < 63) {
|
||||
if (unit.y > 24) {
|
||||
return { x: 55, y: 23 };
|
||||
}
|
||||
return { x: 63, y: 23 };
|
||||
}
|
||||
if (unit.y < 31) {
|
||||
return { x: 64, y: 31 };
|
||||
}
|
||||
if (unit.y < 52) {
|
||||
return { x: 66, y: 52 };
|
||||
}
|
||||
return { x: 66, y: 58 };
|
||||
}
|
||||
const leaderDone = objectives.find((objective) => objective.id === 'pacify-meng-huo-second')?.achieved;
|
||||
if (!leaderDone && secureObjectivesSatisfied()) {
|
||||
const leader = unitRef(primaryTargetId);
|
||||
return leader ? { x: leader.x, y: leader.y } : undefined;
|
||||
}
|
||||
}
|
||||
const objectiveRoutes = {
|
||||
35: [{ id: 'yangping-storehouse', point: { x: 62, y: 28 } }],
|
||||
36: [{ id: 'dingjun-ridge-storehouse', point: { x: 51, y: 35 } }],
|
||||
@@ -2123,6 +2255,32 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
46: [
|
||||
{ id: 'fire-camps', point: { x: 49, y: 42 } },
|
||||
{ id: 'retreat-road', point: { x: 14, y: 55 } }
|
||||
],
|
||||
47: [
|
||||
{ id: 'villages', point: { x: 48, y: 45 } },
|
||||
{ id: 'supply-camps', point: { x: 59, y: 56 } }
|
||||
],
|
||||
48: [
|
||||
{ id: 'amnesty-villages', point: { x: 52, y: 48 } },
|
||||
{ id: 'captive-camps', point: { x: 60, y: 59 } }
|
||||
],
|
||||
49: [
|
||||
{ id: 'amnesty-villages', point: { x: 43, y: 46 } },
|
||||
{ id: 'captive-camps', point: { x: 66, y: 58 } }
|
||||
],
|
||||
50: [
|
||||
{ id: 'clan-villages', point: { x: 82, y: 53 } },
|
||||
{ id: 'captive-camps', point: { x: 68, y: 60 } }
|
||||
],
|
||||
51: [
|
||||
{ id: 'captive-camps', point: { x: 70, y: 62 } },
|
||||
{ id: 'amnesty-route', point: { x: 79, y: 60 } },
|
||||
{ id: 'fire-forts', point: { x: 86, y: 48 } }
|
||||
],
|
||||
52: [
|
||||
{ id: 'clan-villages', point: { x: 52, y: 54 } },
|
||||
{ id: 'hardliner-separation', point: { x: 80, y: 56 } },
|
||||
{ id: 'hardliner-forts', point: { x: 86, y: 46 } }
|
||||
]
|
||||
};
|
||||
const routes = objectiveRoutes[no] ?? [];
|
||||
@@ -2160,7 +2318,7 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
|
||||
function advancePointTile(unit, tiles) {
|
||||
const point = scenarioAdvancePoint(unit);
|
||||
const protectedScenarioPush = no >= 32 && no <= 46;
|
||||
const protectedScenarioPush = no >= 32 && no <= 52;
|
||||
if (!point || (protectedIds.has(unit.id) && !isLastProtectedFighter(unit) && !protectedScenarioPush)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2174,21 +2332,30 @@ async function playBattleWithoutDebugVictory(page, battle) {
|
||||
maxHp: 1
|
||||
};
|
||||
const pathTile = scene.chooseApproachTile?.(unit, routeTarget);
|
||||
if (pathTile && (pathTile.x !== unit.x || pathTile.y !== unit.y)) {
|
||||
if (
|
||||
pathTile &&
|
||||
(pathTile.x !== unit.x || pathTile.y !== unit.y) &&
|
||||
(!shouldAvoidOccupiedRouteTiles || !tileIsOccupiedByOther(unit, pathTile))
|
||||
) {
|
||||
return pathTile;
|
||||
}
|
||||
const currentDistance = distance(unit, point);
|
||||
return tiles
|
||||
.filter((tile) => tile.x !== unit.x || tile.y !== unit.y)
|
||||
.filter((tile) => !shouldAvoidOccupiedRouteTiles || !tileIsOccupiedByOther(unit, tile))
|
||||
.filter((tile) => distance(tile, point) < currentDistance)
|
||||
.sort((a, b) => distance(a, point) - distance(b, point) || (a.cost ?? 0) - (b.cost ?? 0))[0];
|
||||
}
|
||||
|
||||
function chooseMove(unit) {
|
||||
const tiles = [{ x: unit.x, y: unit.y, cost: 0 }, ...scene.reachableTiles(unit)];
|
||||
const reachableTiles = scene.reachableTiles(unit);
|
||||
const tiles = [
|
||||
{ x: unit.x, y: unit.y, cost: 0 },
|
||||
...(shouldAvoidOccupiedRouteTiles ? reachableTiles.filter((tile) => !tileIsOccupiedByOther(unit, tile)) : reachableTiles)
|
||||
];
|
||||
const pressProtected = shouldPressProtected(unit);
|
||||
const canDriveObjective = !protectedIds.has(unit.id) || pressProtected || isLastProtectedFighter(unit);
|
||||
const scenarioRouteTile = no >= 40 && no <= 46 ? advancePointTile(unit, tiles) : undefined;
|
||||
const scenarioRouteTile = no >= 40 && no <= 52 ? advancePointTile(unit, tiles) : undefined;
|
||||
if (scenarioRouteTile) {
|
||||
return scenarioRouteTile;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user