Stabilize early campaign quick objectives

This commit is contained in:
2026-07-05 23:05:35 +09:00
parent 74d5a75ed9
commit aef9fb5d31
4 changed files with 48 additions and 30 deletions

View File

@@ -109,7 +109,7 @@ const earlyCampaignBattles = [
no: 6, no: 6,
id: 'sixth-battle-jieqiao-relief', id: 'sixth-battle-jieqiao-relief',
selected: coreBrothers, selected: coreBrothers,
targets: ['jieqiao-guard-a', 'jieqiao-guard-b', 'jieqiao-archer-c', 'jieqiao-leader-qu-yi'], targets: ['jieqiao-leader-qu-yi'],
protected: ['liu-bei'] protected: ['liu-bei']
}, },
{ {
@@ -1210,6 +1210,13 @@ async function playBattleWithoutDebugVictory(page, battle) {
function canTakeAttack(unit, attack, threat = protectedIds.has(unit.id) ? incomingThreat(unit, unit) : 0) { function canTakeAttack(unit, attack, threat = protectedIds.has(unit.id) ? incomingThreat(unit, unit) : 0) {
if (!protectedIds.has(unit.id)) { if (!protectedIds.has(unit.id)) {
const kill = attack.preview.damage >= attack.target.hp;
if (unit.hp <= Math.ceil(unit.maxHp * 0.35)) {
return kill && threat <= Math.floor(unit.hp * 0.4);
}
if (unit.hp <= Math.ceil(unit.maxHp * 0.55)) {
return kill || threat <= Math.floor(unit.hp * 0.5);
}
return true; return true;
} }
const pressProtected = shouldPressProtected(unit); const pressProtected = shouldPressProtected(unit);
@@ -1253,14 +1260,13 @@ async function playBattleWithoutDebugVictory(page, battle) {
.map((tile) => .map((tile) =>
withUnitAt(unit, tile, () => { withUnitAt(unit, tile, () => {
const attack = attackChoice(unit); const attack = attackChoice(unit);
return attack ? { tile, attack, threat: protectedIds.has(unit.id) ? incomingThreat(unit, tile) : 0 } : undefined; const needsThreatCheck = protectedIds.has(unit.id) || unit.hp <= Math.ceil(unit.maxHp * 0.55);
return attack ? { tile, attack, threat: needsThreatCheck ? incomingThreat(unit, tile) : 0 } : undefined;
}) })
) )
.filter(Boolean); .filter(Boolean);
const preferredAttackingTiles = protectedIds.has(unit.id) const preferredAttackingTiles = attackingTiles.filter((entry) => canTakeAttack(unit, entry.attack, entry.threat));
? attackingTiles.filter((entry) => canTakeAttack(unit, entry.attack, entry.threat))
: attackingTiles;
if (preferredAttackingTiles.length > 0 && (!protectedIds.has(unit.id) || unit.hp >= Math.ceil(unit.maxHp * 0.45))) { if (preferredAttackingTiles.length > 0 && (!protectedIds.has(unit.id) || unit.hp >= Math.ceil(unit.maxHp * 0.45))) {
return preferredAttackingTiles.sort((a, b) => { return preferredAttackingTiles.sort((a, b) => {

View File

@@ -869,7 +869,7 @@ export const fifthBattleScenario: BattleScenarioDefinition = {
openingObjectiveLines: [ openingObjectiveLines: [
'반동탁 연합으로 향하는 길목을 동탁군 장수 호진이 막고 있습니다. 호진을 격파하면 사수관 앞 전초 진지가 흔들립니다.', '반동탁 연합으로 향하는 길목을 동탁군 장수 호진이 막고 있습니다. 호진을 격파하면 사수관 앞 전초 진지가 흔들립니다.',
'중앙 길은 빠르지만 궁병과 기병의 반격을 받기 쉽습니다. 숲길 척후를 처리하고 요새 주변을 차근히 압박하십시오.', '중앙 길은 빠르지만 궁병과 기병의 반격을 받기 쉽습니다. 숲길 척후를 처리하고 요새 주변을 차근히 압박하십시오.',
'14턴 이내에 승리하면 공손찬 진영에서 세 형제의 무공이 더 크게 알려집니다.' '16턴 이내에 승리하면 공손찬 진영에서 세 형제의 무공이 더 크게 알려집니다.'
], ],
tacticalGuide: { tacticalGuide: {
summary: '반동탁 연합으로 향하는 길목에서 호진의 전초 진지를 무너뜨리는 전투입니다.', summary: '반동탁 연합으로 향하는 길목에서 호진의 전초 진지를 무너뜨리는 전투입니다.',
@@ -922,7 +922,7 @@ export const fifthBattleScenario: BattleScenarioDefinition = {
bonds: fifthBattleBonds, bonds: fifthBattleBonds,
mapTextureKey: 'battle-map-fifth', mapTextureKey: 'battle-map-fifth',
leaderUnitId: 'sishui-leader-hu-zhen', leaderUnitId: 'sishui-leader-hu-zhen',
quickVictoryTurnLimit: 14, quickVictoryTurnLimit: 16,
baseVictoryGold: 760, baseVictoryGold: 760,
objectives: [ objectives: [
{ {
@@ -951,9 +951,9 @@ export const fifthBattleScenario: BattleScenarioDefinition = {
{ {
id: 'quick', id: 'quick',
kind: 'quick-victory', kind: 'quick-victory',
label: '14턴 이내 승리', label: '16턴 이내 승리',
rewardGold: 230, rewardGold: 230,
maxTurn: 14 maxTurn: 16
} }
], ],
defeatConditions: [{ kind: 'unit-defeated', unitId: 'liu-bei' }], defeatConditions: [{ kind: 'unit-defeated', unitId: 'liu-bei' }],
@@ -1020,9 +1020,9 @@ export const sixthBattleScenario: BattleScenarioDefinition = {
{ label: '안전 지휘', classKeys: ['lord'], reason: '유비는 후방 배치로 생존과 지휘를 동시에 챙깁니다.' } { label: '안전 지휘', classKeys: ['lord'], reason: '유비는 후방 배치로 생존과 지휘를 동시에 챙깁니다.' }
], ],
deploymentSlots: [ deploymentSlots: [
{ role: 'front', unitId: 'guan-yu', x: 3, y: 16, label: '강가 전열' }, { role: 'front', unitId: 'guan-yu', x: 3, y: 15, label: '강가 전열' },
{ role: 'flank', unitId: 'zhang-fei', x: 2, y: 17, label: '측면 대응' }, { role: 'flank', unitId: 'zhang-fei', x: 2, y: 16, label: '측면 대응' },
{ role: 'support', unitId: 'liu-bei', x: 2, y: 16, label: '후방 지휘' } { role: 'support', unitId: 'liu-bei', x: 2, y: 15, label: '후방 지휘' }
], ],
note: '계교 원군로는 빠른 압박보다 길목 안정이 중요합니다. 유비를 보호하고 관우와 장비로 먼저 맞받으십시오.' note: '계교 원군로는 빠른 압박보다 길목 안정이 중요합니다. 유비를 보호하고 관우와 장비로 먼저 맞받으십시오.'
}, },

View File

@@ -6373,9 +6373,9 @@ export const fifthBattleUnits: UnitData[] = [
]; ];
const sixthBattleAllyPositions: Record<string, { x: number; y: number }> = { const sixthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 2, y: 16 }, 'liu-bei': { x: 2, y: 15 },
'guan-yu': { x: 3, y: 16 }, 'guan-yu': { x: 3, y: 15 },
'zhang-fei': { x: 2, y: 17 } 'zhang-fei': { x: 2, y: 16 }
}; };
export const sixthBattleUnits: UnitData[] = [ export const sixthBattleUnits: UnitData[] = [
@@ -6453,11 +6453,11 @@ export const sixthBattleUnits: UnitData[] = [
classKey: 'cavalry', classKey: 'cavalry',
level: 7, level: 7,
exp: 40, exp: 40,
hp: 37, hp: 35,
maxHp: 37, maxHp: 35,
attack: 12, attack: 11,
move: 5, move: 4,
stats: { might: 78, intelligence: 42, leadership: 60, agility: 78, luck: 52 }, stats: { might: 76, intelligence: 42, leadership: 59, agility: 76, luck: 52 },
equipment: { equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 18 }, weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 18 },
armor: { itemId: 'rebel-vest', level: 2, exp: 8 }, armor: { itemId: 'rebel-vest', level: 2, exp: 8 },
@@ -6516,11 +6516,11 @@ export const sixthBattleUnits: UnitData[] = [
classKey: 'cavalry', classKey: 'cavalry',
level: 7, level: 7,
exp: 42, exp: 42,
hp: 38, hp: 35,
maxHp: 38, maxHp: 35,
attack: 12, attack: 10,
move: 5, move: 4,
stats: { might: 79, intelligence: 42, leadership: 61, agility: 79, luck: 52 }, stats: { might: 76, intelligence: 42, leadership: 60, agility: 76, luck: 52 },
equipment: { equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 20 }, weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 20 },
armor: { itemId: 'rebel-vest', level: 2, exp: 8 }, armor: { itemId: 'rebel-vest', level: 2, exp: 8 },
@@ -6558,11 +6558,11 @@ export const sixthBattleUnits: UnitData[] = [
classKey: 'cavalry', classKey: 'cavalry',
level: 7, level: 7,
exp: 44, exp: 44,
hp: 34, hp: 31,
maxHp: 34, maxHp: 31,
attack: 10, attack: 9,
move: 5, move: 4,
stats: { might: 80, intelligence: 43, leadership: 62, agility: 80, luck: 53 }, stats: { might: 77, intelligence: 43, leadership: 61, agility: 77, luck: 53 },
equipment: { equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 22 }, weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 22 },
armor: { itemId: 'rebel-vest', level: 2, exp: 10 }, armor: { itemId: 'rebel-vest', level: 2, exp: 10 },

View File

@@ -1708,6 +1708,18 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
'xuzhou-guard-b': 'guard', 'xuzhou-guard-b': 'guard',
'xuzhou-archer-c': 'hold', 'xuzhou-archer-c': 'hold',
'xuzhou-leader-xiahou-dun': 'guard', 'xuzhou-leader-xiahou-dun': 'guard',
'jieqiao-skirmisher-a': 'aggressive',
'jieqiao-infantry-a': 'guard',
'jieqiao-archer-a': 'hold',
'jieqiao-cavalry-a': 'aggressive',
'jieqiao-skirmisher-b': 'guard',
'jieqiao-archer-b': 'hold',
'jieqiao-cavalry-b': 'hold',
'jieqiao-guard-a': 'guard',
'jieqiao-cavalry-c': 'guard',
'jieqiao-guard-b': 'guard',
'jieqiao-archer-c': 'hold',
'jieqiao-leader-qu-yi': 'guard',
'xiaopei-raider-a': 'guard', 'xiaopei-raider-a': 'guard',
'xiaopei-raider-b': 'guard', 'xiaopei-raider-b': 'guard',
'xiaopei-infantry-a': 'guard', 'xiaopei-infantry-a': 'guard',