Improve combat UX and story assets

This commit is contained in:
2026-06-24 13:36:26 +09:00
parent 630be9969c
commit 305ead1777
14 changed files with 815 additions and 442 deletions

View File

@@ -1026,6 +1026,13 @@ type BondState = BattleBond & {
battleExp: number;
};
type BondCombatBonus = {
damageBonus: number;
chainRate: number;
label?: string;
partnerIds: string[];
};
type UnitBattleStats = {
damageDealt: number;
damageTaken: number;
@@ -1146,6 +1153,7 @@ type CombatPreview = {
damage: number;
bondDamageBonus: number;
bondLabel?: string;
bondPartnerIds: string[];
equipmentDamageBonus: number;
equipmentDamageReduction: number;
equipmentHitBonus: number;
@@ -3289,8 +3297,8 @@ export class BattleScene extends Phaser.Scene {
this.selectedUnit = undefined;
this.pendingMove = undefined;
this.phase = 'idle';
this.clearMarkers();
this.hideCommandMenu();
this.showEnemyUnitThreatRange(unit);
this.renderUnitDetail(unit, this.enemyDetailMessage(unit));
return;
}
@@ -3377,7 +3385,8 @@ export class BattleScene extends Phaser.Scene {
this.moveUnitView(unit, x, y, view);
}
this.showCommandMenu(unit, pointer?.x ?? targetX, pointer?.y ?? targetY);
this.renderUnitDetail(unit, '공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.');
const finalAllyHint = this.remainingAllyCount() === 1 ? '\n마지막 미행동 장수입니다. 대기를 선택하면 턴 종료 확인이 열립니다.' : '';
this.renderUnitDetail(unit, `공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.${finalAllyHint}`);
}
private canMoveTo(unit: UnitData, x: number, y: number) {
@@ -3503,6 +3512,84 @@ export class BattleScene extends Phaser.Scene {
);
}
private showEnemyUnitThreatRange(enemy: UnitData) {
this.clearMarkers();
const behavior = this.enemyBehavior(enemy);
const moveTiles = this.reachableTiles(enemy);
const threatTiles = this.enemyThreatTilesFor(enemy, moveTiles);
moveTiles.forEach((tile) => this.renderEnemyMoveMarker(enemy, tile.x, tile.y));
threatTiles.forEach((tile) => this.renderEnemyUnitThreatMarker(enemy, tile.x, tile.y));
this.renderSituationPanel(
[
`${enemy.name} 대응 범위`,
`이동 ${moveTiles.length}칸 · 공격 ${threatTiles.length}`,
`${this.enemyBehaviorLabel(behavior)} 태세 · 이동 ${enemy.move} · 사거리 ${this.attackRange(enemy)}`
].join('\n')
);
}
private enemyThreatTilesFor(enemy: UnitData, moveTiles = this.reachableTiles(enemy)) {
const threats = new Map<string, { x: number; y: number }>();
const origins = [{ x: enemy.x, y: enemy.y, cost: 0 }, ...moveTiles];
const range = this.attackRange(enemy);
origins.forEach((origin) => {
this.tilesWithinDistance(origin.x, origin.y, range).forEach(({ x, y }) => {
if (!this.isThreatTargetTile(x, y, enemy.id)) {
return;
}
threats.set(this.tileKey(x, y), { x, y });
});
});
return Array.from(threats.values()).sort((a, b) => a.y - b.y || a.x - b.x);
}
private renderEnemyMoveMarker(enemy: UnitData, x: number, y: number) {
const marker = this.add.rectangle(this.tileTopLeftX(x), this.tileTopLeftY(y), this.layout.tileSize, this.layout.tileSize, palette.blue, 0.16);
marker.setData('tileX', x);
marker.setData('tileY', y);
marker.setData('markerType', 'enemy-move');
marker.setOrigin(0);
marker.setStrokeStyle(1, palette.blue, 0.42);
marker.setDepth(4.15);
if (this.mapMask) {
marker.setMask(this.mapMask);
}
marker.setVisible(this.isTileVisible(x, y));
marker.setInteractive({ useHandCursor: true });
marker.on('pointerover', () => {
marker.setFillStyle(palette.blue, 0.26);
this.renderUnitDetail(enemy, `${this.enemyDetailMessage(enemy)}\n\n이동 가능 칸`);
});
marker.on('pointerout', () => marker.setFillStyle(palette.blue, 0.16));
marker.on('pointerdown', () => this.renderUnitDetail(enemy, `${this.enemyDetailMessage(enemy)}\n\n이동 가능 칸`));
this.markers.push(marker);
}
private renderEnemyUnitThreatMarker(enemy: UnitData, x: number, y: number) {
const behavior = this.enemyBehavior(enemy);
const color = this.threatColor(behavior);
const marker = this.add.rectangle(this.tileTopLeftX(x), this.tileTopLeftY(y), this.layout.tileSize, this.layout.tileSize, color, 0.28);
marker.setData('tileX', x);
marker.setData('tileY', y);
marker.setData('markerType', 'threat');
marker.setOrigin(0);
marker.setStrokeStyle(1, 0xffd1a1, 0.64);
marker.setDepth(4.65);
if (this.mapMask) {
marker.setMask(this.mapMask);
}
marker.setVisible(this.isTileVisible(x, y));
marker.setInteractive({ useHandCursor: true });
marker.on('pointerover', () => {
marker.setFillStyle(color, 0.46);
this.renderUnitDetail(enemy, `${this.enemyDetailMessage(enemy)}\n\n이동 후 공격 가능 칸`);
});
marker.on('pointerout', () => marker.setFillStyle(color, 0.28));
marker.on('pointerdown', () => this.renderUnitDetail(enemy, `${this.enemyDetailMessage(enemy)}\n\n이동 후 공격 가능 칸`));
this.markers.push(marker);
}
private enemyThreatTiles() {
const threats = new Map<string, ThreatTile>();
battleUnits
@@ -4037,6 +4124,7 @@ export class BattleScene extends Phaser.Scene {
this.phase = 'animating';
const result = this.resolveCombatAction(attacker, target, action, false, usable);
this.clearMarkers();
await this.showBondMapEffect(result);
await this.playCombatCutIn(result);
result.counter = this.resolveCounterAttack(result);
if (result.counter) {
@@ -4127,9 +4215,11 @@ export class BattleScene extends Phaser.Scene {
remaining > 0
? `${remaining}명의 아군이 아직 행동할 수 있습니다.`
: '모든 아군이 행동했습니다. 턴 종료 여부를 선택하세요.';
this.renderRosterPanel('ally', `${message}\n${turnHint}`);
const completionMessage = `${message}\n${turnHint}`;
this.renderRosterPanel('ally', completionMessage);
if (remaining === 0) {
this.renderSituationPanel(completionMessage);
this.showTurnEndPrompt(message);
}
}
@@ -5034,15 +5124,16 @@ export class BattleScene extends Phaser.Scene {
return this.tileDistance(user, target) <= usable.range;
}
private combatPreview(attacker: UnitData, defender: UnitData, action: DamageCommand, usable?: BattleUsable): CombatPreview {
private combatPreview(attacker: UnitData, defender: UnitData, action: DamageCommand, usable?: BattleUsable, isCounter = false): CombatPreview {
const terrain = battleMap.terrain[defender.y][defender.x];
const terrainRule = getTerrainRule(terrain);
const attackPower = this.actionPower(attacker, action, usable);
const defensePower = this.actionDefense(defender, action, terrainRule.defenseBonus);
const bondBonus = attacker.faction === 'ally' ? this.strongestBondBonus(attacker.id) : { damageBonus: 0, chainRate: 0 };
const equipmentEffect = this.combatEquipmentEffect(attacker, defender, action, usable);
const bondBonus = this.activeBondCombatBonus(attacker, defender, action, isCounter);
const equipmentEffect = this.combatEquipmentEffect(attacker, defender, action, usable, bondBonus);
const totalDamageBonus = bondBonus.damageBonus + equipmentEffect.damageBonus - equipmentEffect.damageReduction;
const damage = Phaser.Math.Clamp(Math.round((attackPower - defensePower) * (1 + totalDamageBonus / 100)), 3, defender.maxHp);
const baseDamage = Phaser.Math.Clamp(Math.round((attackPower - defensePower) * (1 + totalDamageBonus / 100)), 3, defender.maxHp);
const damage = isCounter ? Phaser.Math.Clamp(Math.round(baseDamage * 0.45), 1, defender.maxHp) : baseDamage;
const defenderTerrainRating = getUnitClass(defender.classKey).terrainRatings[terrain] ?? 100;
const terrainHitPenalty = Math.floor(terrainRule.defenseBonus / 2) + Math.max(0, Math.floor((defenderTerrainRating - 100) / 4));
const hitRate = Phaser.Math.Clamp(
@@ -5076,6 +5167,7 @@ export class BattleScene extends Phaser.Scene {
damage,
bondDamageBonus: bondBonus.damageBonus,
bondLabel: bondBonus.label,
bondPartnerIds: bondBonus.partnerIds,
equipmentDamageBonus: equipmentEffect.damageBonus,
equipmentDamageReduction: equipmentEffect.damageReduction,
equipmentHitBonus: equipmentEffect.hitBonus,
@@ -5122,7 +5214,7 @@ export class BattleScene extends Phaser.Scene {
}
private resolveCombatAction(attacker: UnitData, defender: UnitData, action: DamageCommand, isCounter = false, usable?: BattleUsable): CombatResult {
const preview = this.combatPreview(attacker, defender, action, usable);
const preview = this.combatPreview(attacker, defender, action, usable, isCounter);
const previousDefenderHp = defender.hp;
const hit = this.rollPercent(preview.hitRate);
const critical = hit && this.rollPercent(preview.criticalRate);
@@ -5245,7 +5337,13 @@ export class BattleScene extends Phaser.Scene {
return Math.min(target.maxHp - target.hp, Math.max(1, usable.power + bonus + equipmentBonus));
}
private combatEquipmentEffect(attacker: UnitData, defender: UnitData, action: DamageCommand, usable?: BattleUsable) {
private combatEquipmentEffect(
attacker: UnitData,
defender: UnitData,
action: DamageCommand,
usable?: BattleUsable,
bondBonus: BondCombatBonus = this.activeBondCombatBonus(attacker, defender, action)
) {
const labels: string[] = [];
let damageBonus = 0;
let damageReduction = 0;
@@ -5256,7 +5354,6 @@ export class BattleScene extends Phaser.Scene {
const armor = getItem(defender.equipment.armor.itemId);
const accessory = getItem(attacker.equipment.accessory.itemId);
const defenderAccessory = getItem(defender.equipment.accessory.itemId);
const bondBonus = attacker.faction === 'ally' ? this.strongestBondBonus(attacker.id) : { damageBonus: 0, chainRate: 0 };
if (action === 'attack' && weapon.id === 'twin-oath-blades' && bondBonus.chainRate > 0) {
damageBonus += 6;
@@ -5387,7 +5484,7 @@ export class BattleScene extends Phaser.Scene {
private formatCounterLine(result: CombatResult) {
const outcome = result.hit ? `${result.critical ? '치명타로 ' : ''}${result.damage} 피해` : '회피됨';
const defeated = result.defeated ? ` / ${result.defender.name} 퇴각` : ` / ${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
return `반격: ${result.attacker.name} -> ${result.defender.name} ${outcome}${defeated}`;
return `반격(감소): ${result.attacker.name} -> ${result.defender.name} ${outcome}${defeated}`;
}
private combatGrowthLines(result: CombatResult) {
@@ -7114,6 +7211,69 @@ export class BattleScene extends Phaser.Scene {
return container;
}
private showBondMapEffect(result: CombatResult) {
if (result.bondDamageBonus <= 0 || result.bondPartnerIds.length === 0) {
return Promise.resolve();
}
const attackerView = this.unitViews.get(result.attacker.id);
if (!attackerView || !this.isTileVisible(result.attacker.x, result.attacker.y)) {
return Promise.resolve();
}
const partnerUnits = result.bondPartnerIds
.map((partnerId) => battleUnits.find((unit) => unit.id === partnerId && unit.hp > 0))
.filter((unit): unit is UnitData => Boolean(unit));
const visiblePartners = partnerUnits.filter((unit) => this.isTileVisible(unit.x, unit.y) && this.unitViews.has(unit.id));
if (visiblePartners.length === 0) {
return Promise.resolve();
}
soundDirector.playEffect('strategy-cast', { volume: 0.18, rate: 1.14, stopAfterMs: 520 });
const objects: Phaser.GameObjects.GameObject[] = [];
const line = this.add.graphics();
line.setDepth(34);
line.lineStyle(4, 0xffdf7b, 0.78);
visiblePartners.forEach((partner) => {
const partnerView = this.unitViews.get(partner.id);
if (!partnerView) {
return;
}
line.beginPath();
line.moveTo(attackerView.sprite.x, attackerView.sprite.y - this.layout.tileSize * 0.28);
line.lineTo(partnerView.sprite.x, partnerView.sprite.y - this.layout.tileSize * 0.28);
line.strokePath();
objects.push(this.createBondBadge(partnerView.sprite.x, partnerView.sprite.y - this.layout.tileSize * 0.86, `${partner.name} ♥ 공명`, 35));
});
objects.push(line);
objects.push(this.createBondBadge(attackerView.sprite.x, attackerView.sprite.y - this.layout.tileSize * 0.96, '연계 발동', 36));
this.tweens.add({ targets: objects, alpha: 0, y: '-=12', duration: 620, ease: 'Sine.easeOut' });
return this.delay(660).then(() => objects.forEach((object) => object.destroy()));
}
private createBondBadge(x: number, y: number, label: string, depth: number) {
const container = this.add.container(x, y);
container.setDepth(depth);
const width = Math.max(84, label.length * 13 + 22);
const bg = this.add.rectangle(0, 0, width, 30, 0x2a1820, 0.92);
bg.setStrokeStyle(2, 0xffdf7b, 0.92);
const glow = this.add.circle(-width / 2 + 17, 0, 10, 0xff6f91, 0.82);
const text = this.add.text(5, 0, label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '14px',
color: '#fff2b8',
fontStyle: '700',
stroke: '#30100d',
strokeThickness: 3
});
text.setOrigin(0.5);
container.add([bg, glow, text]);
this.tweens.add({ targets: glow, scale: 1.35, alpha: 0.28, yoyo: true, repeat: 1, duration: 180 });
return container;
}
private showBondCombatEffect(result: CombatResult, textX: number, textY: number, auraX: number, auraY: number, depth: number) {
if (!result.bondLabel || result.bondDamageBonus <= 0) {
return Promise.resolve();
@@ -7503,9 +7663,7 @@ export class BattleScene extends Phaser.Scene {
}
private recordBondAttackIntent(unit: UnitData, target: UnitData) {
const partners = this.attackIntents
.filter((intent) => intent.targetId === target.id && intent.attackerId !== unit.id)
.map((intent) => intent.attackerId);
const partners = this.activeBondCombatBonus(unit, target, 'attack').partnerIds;
const growthResults: BondGrowthResult[] = [];
partners.forEach((partnerId) => {
@@ -7549,9 +7707,8 @@ export class BattleScene extends Phaser.Scene {
return `${unit.name} 공격 명령 완료\n공격 가능한 적이 없습니다.\n${this.formatEquipmentGrowth(weaponGrowth)}`;
}
const partners = this.attackIntents
.filter((intent) => intent.targetId === target.id && intent.attackerId !== unit.id)
.map((intent) => intent.attackerId);
const bondBonus = this.activeBondCombatBonus(unit, target, 'attack');
const partners = bondBonus.partnerIds;
let gained = 0;
partners.forEach((partnerId) => {
@@ -7571,8 +7728,7 @@ export class BattleScene extends Phaser.Scene {
this.attackIntents = this.attackIntents.filter((intent) => intent.attackerId !== unit.id);
this.attackIntents.push({ attackerId: unit.id, targetId: target.id });
const bonus = this.strongestBondBonus(unit.id);
const chain = bonus.chainRate > 0 ? `\n공명 보너스: 피해 +${bonus.damageBonus}% / 연계 ${bonus.chainRate}%` : '';
const chain = bondBonus.chainRate > 0 ? `\n공명 보너스: 피해 +${bondBonus.damageBonus}% / 연계 ${bondBonus.chainRate}%` : '';
const exp = gained > 0 ? `\n같은 목표를 노린 공명 경험치 +${gained}` : '\n같은 목표를 노린 장수가 생기면 공명 경험치가 쌓입니다.';
return `${unit.name} 공격 명령 완료\n예상 목표: ${target.name}\n${this.formatEquipmentGrowth(weaponGrowth)}${chain}${exp}`;
}
@@ -7702,6 +7858,47 @@ export class BattleScene extends Phaser.Scene {
});
}
private activeBondCombatBonus(attacker: UnitData, defender: UnitData, action: DamageCommand, isCounter = false): BondCombatBonus {
if (isCounter || action !== 'attack' || attacker.faction !== 'ally') {
return { damageBonus: 0, chainRate: 0, partnerIds: [] };
}
const candidates = Array.from(this.bondStates.values())
.map((bond) => {
if (!bond.unitIds.includes(attacker.id)) {
return undefined;
}
const partnerId = bond.unitIds.find((unitId) => unitId !== attacker.id);
const partner = partnerId ? battleUnits.find((unit) => unit.id === partnerId && unit.hp > 0) : undefined;
if (!partner || partner.faction !== attacker.faction || !this.canBondPartnerJoinAttack(attacker, partner, defender)) {
return undefined;
}
return { bond, partner };
})
.filter((candidate): candidate is { bond: BondState; partner: UnitData } => Boolean(candidate))
.sort((a, b) => b.bond.level - a.bond.level);
const strongest = candidates[0];
if (!strongest) {
return { damageBonus: 0, chainRate: 0, partnerIds: [] };
}
const [first, second] = strongest.bond.unitIds.map((id) => this.unitName(id));
return {
damageBonus: Math.floor(strongest.bond.level / 20) * 3,
chainRate: strongest.bond.level >= 70 ? 18 : strongest.bond.level >= 50 ? 8 : 0,
label: `${strongest.bond.title}: ${first}·${second}`,
partnerIds: [strongest.partner.id]
};
}
private canBondPartnerJoinAttack(attacker: UnitData, partner: UnitData, defender: UnitData) {
if (this.tileDistance(attacker, partner) <= 1) {
return true;
}
return this.canUseDamageCommand(partner, defender, 'attack');
}
private strongestBondBonus(unitId: string) {
const related = Array.from(this.bondStates.values()).filter((bond) => bond.unitIds.includes(unitId));
const strongest = related.sort((a, b) => b.level - a.level)[0];
@@ -8858,9 +9055,14 @@ export class BattleScene extends Phaser.Scene {
activeFaction: this.activeFaction,
phase: this.phase,
battleOutcome: this.battleOutcome ?? null,
mapTextureKey: battleScenario.mapTextureKey,
mapTextureReady: this.textures.exists(battleScenario.mapTextureKey),
mapBackgroundReady: this.mapBackground?.texture.key === battleScenario.mapTextureKey,
resultVisible: this.resultObjects.length > 0,
turnPromptVisible: this.turnPromptObjects.length > 0,
markerCount: this.markers.length,
threatMarkerCount: this.markers.filter((marker) => marker.getData('markerType') === 'threat').length,
enemyMoveMarkerCount: this.markers.filter((marker) => marker.getData('markerType') === 'enemy-move').length,
camera: {
x: this.cameraTileX,
y: this.cameraTileY,
@@ -8887,6 +9089,7 @@ export class BattleScene extends Phaser.Scene {
itemStocks: this.serializeItemStocks(),
battleBuffs: Array.from(this.battleBuffs.values()).map((buff) => ({ ...buff })),
treasureEffectPreviews: this.debugTreasureEffectPreviews(),
combatMechanicsProbe: this.debugCombatMechanicsProbe(),
units: battleUnits.map((unit) => ({
id: unit.id,
name: unit.name,
@@ -8914,6 +9117,61 @@ export class BattleScene extends Phaser.Scene {
};
}
debugUnitById(unitId: string) {
if (!import.meta.env.DEV) {
return undefined;
}
return battleUnits.find((unit) => unit.id === unitId);
}
private debugCombatMechanicsProbe() {
const bond = Array.from(this.bondStates.values()).find((candidate) => {
const units = candidate.unitIds.map((unitId) => battleUnits.find((unit) => unit.id === unitId && unit.hp > 0));
return units.length === 2 && units.every((unit) => unit?.faction === 'ally') && Math.floor(candidate.level / 20) * 3 > 0;
});
const attacker = bond ? battleUnits.find((unit) => unit.id === bond.unitIds[0] && unit.hp > 0) : undefined;
const partner = bond ? battleUnits.find((unit) => unit.id === bond.unitIds[1] && unit.hp > 0) : undefined;
const enemy = battleUnits.find((unit) => unit.faction === 'enemy' && unit.hp > 0);
if (!bond || !attacker || !partner || !enemy) {
return null;
}
const direction = partner.x <= battleMap.width - 3 ? 1 : -1;
const nearX = partner.x + direction;
const edgeX = partner.x + direction * 2;
if (!this.isInBounds(nearX, partner.y) || !this.isInBounds(edgeX, partner.y)) {
return null;
}
const farAttackerX = Math.abs(battleMap.width - 2 - partner.x) > 2 ? battleMap.width - 2 : 1;
const farDefenderX = farAttackerX > 0 ? farAttackerX - 1 : farAttackerX + 1;
if (!this.isInBounds(farAttackerX, partner.y) || !this.isInBounds(farDefenderX, partner.y)) {
return null;
}
const adjacentPreview = this.combatPreview({ ...attacker, x: nearX, y: partner.y }, { ...enemy, x: edgeX, y: partner.y }, 'attack');
const sameTargetPreview = this.combatPreview({ ...attacker, x: edgeX, y: partner.y }, { ...enemy, x: nearX, y: partner.y }, 'attack');
const farPreview = this.combatPreview({ ...attacker, x: farAttackerX, y: partner.y }, { ...enemy, x: farDefenderX, y: partner.y }, 'attack');
const normalIncomingPreview = this.combatPreview(enemy, attacker, 'attack');
const counterIncomingPreview = this.combatPreview(enemy, attacker, 'attack', undefined, true);
return {
bondId: bond.id,
attackerId: attacker.id,
partnerId: partner.id,
enemyId: enemy.id,
adjacentBondDamageBonus: adjacentPreview.bondDamageBonus,
adjacentPartnerIds: adjacentPreview.bondPartnerIds,
sameTargetBondDamageBonus: sameTargetPreview.bondDamageBonus,
sameTargetPartnerIds: sameTargetPreview.bondPartnerIds,
farBondDamageBonus: farPreview.bondDamageBonus,
farPartnerIds: farPreview.bondPartnerIds,
normalDamage: normalIncomingPreview.damage,
counterDamage: counterIncomingPreview.damage
};
}
private debugTreasureEffectPreviews() {
return ['liu-bei', 'guan-yu', 'zhang-fei', 'zhuge-liang']
.map((unitId) => battleUnits.find((unit) => unit.id === unitId && unit.hp > 0))

View File

@@ -2,11 +2,22 @@ import Phaser from 'phaser';
import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png';
import liuBeiPortraitUrl from '../../assets/images/portraits/liu-bei.png';
import zhangFeiPortraitUrl from '../../assets/images/portraits/zhang-fei.png';
import { battleMapAssets } from '../data/battleMapAssets';
import storyChaosUrl from '../../assets/images/story/01-yellow-turban-chaos.png';
import storyLiuBeiUrl from '../../assets/images/story/02-liu-bei-resolve.png';
import storyThreeHeroesUrl from '../../assets/images/story/03-three-heroes-meet.png';
import storyMilitiaUrl from '../../assets/images/story/04-volunteer-militia.png';
import storySortieUrl from '../../assets/images/story/05-first-sortie.png';
import storyYellowPursuitUrl from '../../assets/images/story/06-yellow-turban-pursuit.png';
import storyAntiDongPassUrl from '../../assets/images/story/07-anti-dong-pass.png';
import storyXuzhouHandoverUrl from '../../assets/images/story/08-xuzhou-handover.png';
import storyWanderingRoadUrl from '../../assets/images/story/09-wandering-road.png';
import storyWolongCottageUrl from '../../assets/images/story/10-wolong-cottage.png';
import storyRedCliffsFireUrl from '../../assets/images/story/11-red-cliffs-fire.png';
import storyYizhouMountainPassUrl from '../../assets/images/story/12-yizhou-mountain-pass.png';
import storyNorthernExpeditionUrl from '../../assets/images/story/13-northern-expedition.png';
import storyNanzhongPacificationUrl from '../../assets/images/story/14-nanzhong-pacification.png';
import storyYilingBaidiAftermathUrl from '../../assets/images/story/15-yiling-baidi-aftermath.png';
import titleBackgroundUrl from '../../assets/images/taoyuan-oath-title.png';
import guanYuActionSheetUrl from '../../assets/images/units/unit-guan-yu-actions.png';
import guanYuUnitSheetUrl from '../../assets/images/units/unit-guan-yu.png';
@@ -58,10 +69,25 @@ export class BootScene extends Phaser.Scene {
this.load.image('story-three-heroes', storyThreeHeroesUrl);
this.load.image('story-militia', storyMilitiaUrl);
this.load.image('story-sortie', storySortieUrl);
this.load.image('story-yellow-pursuit', storyYellowPursuitUrl);
this.load.image('story-anti-dong', storyAntiDongPassUrl);
this.load.image('story-xuzhou', storyXuzhouHandoverUrl);
this.load.image('story-wandering', storyWanderingRoadUrl);
this.load.image('story-wolong', storyWolongCottageUrl);
this.load.image('story-red-cliffs', storyRedCliffsFireUrl);
this.load.image('story-yizhou', storyYizhouMountainPassUrl);
this.load.image('story-northern', storyNorthernExpeditionUrl);
this.load.image('story-nanzhong', storyNanzhongPacificationUrl);
this.load.image('story-yiling-baidi', storyYilingBaidiAftermathUrl);
this.load.image('story-resolve', storyLiuBeiUrl);
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
Object.entries(battleMapAssets).forEach(([key, url]) => {
this.load.image(key, url);
});
unitSheets.forEach(({ key, url, actionKey, actionUrl }) => {
this.load.spritesheet(key, url, {
frameWidth: unitSheetFrameSize,