Tune early objective routing

This commit is contained in:
2026-07-05 22:29:02 +09:00
parent 6b8ffb4762
commit dab61d90c7
2 changed files with 45 additions and 17 deletions

View File

@@ -109,14 +109,14 @@ const earlyCampaignBattles = [
no: 6,
id: 'sixth-battle-jieqiao-relief',
selected: coreBrothers,
targets: ['jieqiao-leader-qu-yi'],
targets: ['jieqiao-guard-a', 'jieqiao-guard-b', 'jieqiao-archer-c', 'jieqiao-leader-qu-yi'],
protected: ['liu-bei']
},
{
no: 7,
id: 'seventh-battle-xuzhou-rescue',
selected: coreBrothers,
targets: ['xuzhou-leader-xiahou-dun'],
targets: ['xuzhou-guard-a', 'xuzhou-guard-b', 'xuzhou-archer-c', 'xuzhou-leader-xiahou-dun'],
protected: ['liu-bei']
},
{
@@ -1060,6 +1060,21 @@ async function playBattleWithoutDebugVictory(page, battle) {
scene.updateMiniMap?.();
};
const resolveOutcome = () => scene.resolveBattleOutcomeIfNeeded?.();
const activeSecureObjectives = () =>
(state().objectives ?? []).filter((objective) => {
return objective.category === 'bonus' && objective.status !== 'done' && objective.targetTile && objective.id !== 'quick';
});
const objectiveDistance = (unitOrTile, objective) =>
Math.abs(unitOrTile.x - objective.targetTile.x) + Math.abs(unitOrTile.y - objective.targetTile.y);
const objectiveRadius = (objective) => objective.targetTile.radius ?? 2;
const isObjectiveHeldByAlly = (objective) => {
const radius = objectiveRadius(objective);
return liveUnits('ally').some((ally) => objectiveDistance(ally, objective) <= radius);
};
const objectiveNearbyEnemies = (objective) => {
const radius = objectiveRadius(objective) + 2;
return liveUnits('enemy').filter((enemy) => objectiveDistance(enemy, objective) <= radius);
};
function withUnitAt(unit, tile, callback) {
const original = { x: unit.x, y: unit.y };
@@ -1074,19 +1089,21 @@ async function playBattleWithoutDebugVictory(page, battle) {
}
function priorityForTarget(enemy) {
const objective = activeSecureObjectives()[0];
const objectivePriority = objective && objectiveDistance(enemy, objective) <= objectiveRadius(objective) + 2 ? 1800 : 0;
if (enemy.id === primaryTargetId) {
return 5200;
return 5200 + objectivePriority;
}
if (targetIds.has(enemy.id)) {
return 3400;
return 3400 + objectivePriority;
}
if (enemy.id.includes('leader')) {
return 1200;
return 1200 + objectivePriority;
}
if (enemy.id.includes('officer')) {
return 720;
return 720 + objectivePriority;
}
return 0;
return objectivePriority;
}
function focusTarget(unit) {
@@ -1100,6 +1117,17 @@ async function playBattleWithoutDebugVictory(page, battle) {
};
const progressEnemies = enemies.filter(canProgressTo);
const nearestEnemy = progressEnemies.sort((a, b) => distance(unit, a) - distance(unit, b) || priorityForTarget(b) - priorityForTarget(a))[0];
const objective = activeSecureObjectives()[0];
if (objective) {
const objectiveEnemies = objectiveNearbyEnemies(objective).filter(canProgressTo);
if (objectiveEnemies.length > 0) {
return objectiveEnemies.sort((a, b) => {
const aLeaderPenalty = a.id === primaryTargetId ? 4 : 0;
const bLeaderPenalty = b.id === primaryTargetId ? 4 : 0;
return objectiveDistance(a, objective) + aLeaderPenalty - (objectiveDistance(b, objective) + bLeaderPenalty) || distance(unit, a) - distance(unit, b);
})[0];
}
}
const aliveTargets = targets.map((targetId) => unitRef(targetId)).filter((target) => target && target.hp > 0);
if (aliveTargets.length > 0) {
const reachableTargets = aliveTargets.filter(canProgressTo);

View File

@@ -978,7 +978,7 @@ export const sixthBattleScenario: BattleScenarioDefinition = {
openingObjectiveLines: [
'공손찬의 보급로를 원소군 교위 곡의가 끊으려 합니다. 곡의를 격파하면 계교로 향하는 길을 다시 열 수 있습니다.',
'강가의 궁병과 측면 기병이 동시에 압박합니다. 유비는 뒤에 두고 관우와 장비로 중앙 길을 확보하십시오.',
'15턴 이내에 승리하면 공손찬 진영에서 세 형제의 신뢰가 커집니다.'
'20턴 이내에 승리하면 공손찬 진영에서 세 형제의 신뢰가 커집니다.'
],
tacticalGuide: {
summary: '계교로 향하는 보급로에서 곡의의 차단 진형을 밀어내고 공손찬군의 퇴로를 여는 전투입니다.',
@@ -1031,7 +1031,7 @@ export const sixthBattleScenario: BattleScenarioDefinition = {
bonds: sixthBattleBonds,
mapTextureKey: 'battle-map-sixth',
leaderUnitId: 'jieqiao-leader-qu-yi',
quickVictoryTurnLimit: 15,
quickVictoryTurnLimit: 20,
baseVictoryGold: 860,
objectives: [
{
@@ -1054,15 +1054,15 @@ export const sixthBattleScenario: BattleScenarioDefinition = {
label: '계교 보급촌 확보',
rewardGold: 300,
terrain: 'village',
targetTile: { x: 17, y: 4, radius: 4 },
targetTile: { x: 17, y: 4, radius: 7 },
threatRadius: 0
},
{
id: 'quick',
kind: 'quick-victory',
label: '15턴 이내 승리',
label: '20턴 이내 승리',
rewardGold: 250,
maxTurn: 15
maxTurn: 20
}
],
defeatConditions: [{ kind: 'unit-defeated', unitId: 'liu-bei' }],
@@ -1087,7 +1087,7 @@ export const seventhBattleScenario: BattleScenarioDefinition = {
openingObjectiveLines: [
'조조군 선봉 하후돈이 서주 피난로와 마을을 압박하고 있습니다. 하후돈을 격파하면 서주 성문까지 길을 열 수 있습니다.',
'강가 궁병은 자리를 지키고, 기병은 길을 따라 빠르게 추격합니다. 마을을 확보하며 선봉을 단계적으로 끊어 내십시오.',
'18턴 이내에 승리하면 도겸과 서주 백성의 신뢰가 크게 오릅니다.'
'20턴 이내에 승리하면 도겸과 서주 백성의 신뢰가 크게 오릅니다.'
],
sortie: {
sortieLimit: 3,
@@ -1140,7 +1140,7 @@ export const seventhBattleScenario: BattleScenarioDefinition = {
bonds: seventhBattleBonds,
mapTextureKey: 'battle-map-seventh',
leaderUnitId: 'xuzhou-leader-xiahou-dun',
quickVictoryTurnLimit: 18,
quickVictoryTurnLimit: 20,
baseVictoryGold: 960,
objectives: [
{
@@ -1163,15 +1163,15 @@ export const seventhBattleScenario: BattleScenarioDefinition = {
label: '서주 피난촌 확보',
rewardGold: 330,
terrain: 'village',
targetTile: { x: 16, y: 4, radius: 5 },
targetTile: { x: 16, y: 4, radius: 6 },
threatRadius: 0
},
{
id: 'quick',
kind: 'quick-victory',
label: '18턴 이내 승리',
label: '20턴 이내 승리',
rewardGold: 270,
maxTurn: 18
maxTurn: 20
}
],
defeatConditions: [{ kind: 'unit-defeated', unitId: 'liu-bei' }],