diff --git a/scripts/verify-flow.mjs b/scripts/verify-flow.mjs index c7ddc96..27024bd 100644 --- a/scripts/verify-flow.mjs +++ b/scripts/verify-flow.mjs @@ -9084,9 +9084,11 @@ try { ); if ( weishuiCampsStoryState?.background !== 'story-weishui-camps' || - weishuiCampsStoryState?.backgroundReady !== true + weishuiCampsStoryState?.backgroundReady !== true || + weishuiCampsStoryState?.portraitKey !== 'zhugeLiang' || + weishuiCampsStoryState?.portraitTextureKey !== 'portrait-zhuge-liang' ) { - throw new Error(`Expected Weishui camps story to lazy-load its new generated background: ${JSON.stringify(weishuiCampsStoryState)}`); + throw new Error(`Expected Weishui camps story to lazy-load its new generated background and show Zhuge Liang portrait by speaker fallback: ${JSON.stringify(weishuiCampsStoryState)}`); } await page.screenshot({ path: 'dist/verification-sixty-fourth-story.png', fullPage: true }); @@ -9127,11 +9129,11 @@ try { !sixtyFourthEnemyBehaviors.has('aggressive') || !sixtyFourthEnemyBehaviors.has('guard') || !sixtyFourthEnemyBehaviors.has('hold') || - !sixtyFourthEnemies.some((unit) => unit.id === 'northern-tenth-leader-sima-yi') || + !sixtyFourthEnemies.some((unit) => unit.id === 'northern-tenth-leader-sima-yi' && unit.combatPortraitKey === 'portrait-sima-yi') || !sixtyFourthEnemies.some((unit) => unit.id === 'northern-tenth-officer-zhang-he') || !sixtyFourthEnemies.some((unit) => unit.id === 'northern-tenth-officer-guo-huai') || - !sixtyFourthAllies.some((unit) => unit.id === 'zhuge-liang') || - !sixtyFourthAllies.some((unit) => unit.id === 'jiang-wei') || + !sixtyFourthAllies.some((unit) => unit.id === 'zhuge-liang' && unit.combatPortraitKey === 'portrait-zhuge-liang') || + !sixtyFourthAllies.some((unit) => unit.id === 'jiang-wei' && unit.combatPortraitKey === 'portrait-jiang-wei') || !sixtyFourthAllies.some((unit) => unit.id === 'wang-ping') || !sixtyFourthAllies.some((unit) => unit.id === 'huang-quan') || !sixtyFourthAllies.some((unit) => unit.id === 'li-yan') || diff --git a/src/assets/images/portraits/jiang-wei.png b/src/assets/images/portraits/jiang-wei.png new file mode 100644 index 0000000..4797d1c Binary files /dev/null and b/src/assets/images/portraits/jiang-wei.png differ diff --git a/src/assets/images/portraits/sima-yi.png b/src/assets/images/portraits/sima-yi.png new file mode 100644 index 0000000..9716dad Binary files /dev/null and b/src/assets/images/portraits/sima-yi.png differ diff --git a/src/assets/images/portraits/zhao-yun.png b/src/assets/images/portraits/zhao-yun.png new file mode 100644 index 0000000..515b4aa Binary files /dev/null and b/src/assets/images/portraits/zhao-yun.png differ diff --git a/src/assets/images/portraits/zhuge-liang.png b/src/assets/images/portraits/zhuge-liang.png new file mode 100644 index 0000000..fa8b83c Binary files /dev/null and b/src/assets/images/portraits/zhuge-liang.png differ diff --git a/src/game/data/scenario.ts b/src/game/data/scenario.ts index 7705a7a..76b6965 100644 --- a/src/game/data/scenario.ts +++ b/src/game/data/scenario.ts @@ -1,7 +1,7 @@ import type { TerrainType, UnitClassKey } from './battleRules'; import type { EquipmentSet } from './battleItems'; -export type PortraitKey = 'liuBei' | 'guanYu' | 'zhangFei'; +export type PortraitKey = 'liuBei' | 'guanYu' | 'zhangFei' | 'zhugeLiang' | 'zhaoYun' | 'jiangWei' | 'simaYi'; export type StoryPage = { id: string; diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index aa6560a..dd09043 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -7231,6 +7231,18 @@ export class BattleScene extends Phaser.Scene { if (unit.id === 'zhang-fei') { return 'portrait-zhang-fei'; } + if (unit.id === 'zhuge-liang') { + return 'portrait-zhuge-liang'; + } + if (unit.id === 'zhao-yun') { + return 'portrait-zhao-yun'; + } + if (unit.id === 'jiang-wei') { + return 'portrait-jiang-wei'; + } + if (unit.id.includes('sima-yi') || unit.name === '사마의') { + return 'portrait-sima-yi'; + } return undefined; } @@ -9277,6 +9289,7 @@ export class BattleScene extends Phaser.Scene { classKey: unit.classKey, textureBase: this.unitViews.get(unit.id)?.textureBase ?? this.unitTextureKey(unit), actionTexture: this.unitActionTexture(unit), + combatPortraitKey: this.combatPortraitKey(unit) ?? null, ai: unit.faction === 'enemy' ? this.enemyBehavior(unit) : null, attackRange: this.attackRange(unit), level: unit.level, diff --git a/src/game/scenes/BootScene.ts b/src/game/scenes/BootScene.ts index 88dab36..b2e3fe0 100644 --- a/src/game/scenes/BootScene.ts +++ b/src/game/scenes/BootScene.ts @@ -1,7 +1,11 @@ import Phaser from 'phaser'; import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png'; +import jiangWeiPortraitUrl from '../../assets/images/portraits/jiang-wei.png'; import liuBeiPortraitUrl from '../../assets/images/portraits/liu-bei.png'; +import simaYiPortraitUrl from '../../assets/images/portraits/sima-yi.png'; +import zhaoYunPortraitUrl from '../../assets/images/portraits/zhao-yun.png'; import zhangFeiPortraitUrl from '../../assets/images/portraits/zhang-fei.png'; +import zhugeLiangPortraitUrl from '../../assets/images/portraits/zhuge-liang.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'; @@ -77,6 +81,10 @@ export class BootScene extends Phaser.Scene { this.load.image('portrait-liu-bei', liuBeiPortraitUrl); this.load.image('portrait-guan-yu', guanYuPortraitUrl); this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl); + this.load.image('portrait-zhuge-liang', zhugeLiangPortraitUrl); + this.load.image('portrait-zhao-yun', zhaoYunPortraitUrl); + this.load.image('portrait-jiang-wei', jiangWeiPortraitUrl); + this.load.image('portrait-sima-yi', simaYiPortraitUrl); sourceUnitSheets.forEach(({ key, url, actionKey, actionUrl }) => { this.load.spritesheet(key, url, { diff --git a/src/game/scenes/CampScene.ts b/src/game/scenes/CampScene.ts index 9a4637b..d0db9f2 100644 --- a/src/game/scenes/CampScene.ts +++ b/src/game/scenes/CampScene.ts @@ -255,13 +255,20 @@ type CampaignTimelineChapter = { const portraitKeys: Record = { liuBei: 'portrait-liu-bei', guanYu: 'portrait-guan-yu', - zhangFei: 'portrait-zhang-fei' + zhangFei: 'portrait-zhang-fei', + zhugeLiang: 'portrait-zhuge-liang', + zhaoYun: 'portrait-zhao-yun', + jiangWei: 'portrait-jiang-wei', + simaYi: 'portrait-sima-yi' }; const portraitByUnitId: Record = { 'liu-bei': 'liuBei', 'guan-yu': 'guanYu', - 'zhang-fei': 'zhangFei' + 'zhang-fei': 'zhangFei', + 'zhuge-liang': 'zhugeLiang', + 'zhao-yun': 'zhaoYun', + 'jiang-wei': 'jiangWei' }; const campSupplies: CampSupplyDefinition[] = [ diff --git a/src/game/scenes/StoryScene.ts b/src/game/scenes/StoryScene.ts index cd73a7e..80b85d6 100644 --- a/src/game/scenes/StoryScene.ts +++ b/src/game/scenes/StoryScene.ts @@ -7,7 +7,21 @@ import { palette } from '../ui/palette'; const portraitKeys: Record = { liuBei: 'portrait-liu-bei', guanYu: 'portrait-guan-yu', - zhangFei: 'portrait-zhang-fei' + zhangFei: 'portrait-zhang-fei', + zhugeLiang: 'portrait-zhuge-liang', + zhaoYun: 'portrait-zhao-yun', + jiangWei: 'portrait-jiang-wei', + simaYi: 'portrait-sima-yi' +}; + +const portraitBySpeaker: Record = { + 유비: 'liuBei', + 관우: 'guanYu', + 장비: 'zhangFei', + 제갈량: 'zhugeLiang', + 조운: 'zhaoYun', + 강유: 'jiangWei', + 사마의: 'simaYi' }; type AlphaObject = Phaser.GameObjects.Image | Phaser.GameObjects.Rectangle | Phaser.GameObjects.Text; @@ -57,7 +71,9 @@ export class StoryScene extends Phaser.Scene { currentPageId: page?.id, background: page?.background, backgroundReady: page ? this.textures.exists(page.background) : false, - activeTexture: this.background?.texture.key ?? null + activeTexture: this.background?.texture.key ?? null, + portraitKey: page ? this.pagePortraitKey(page) ?? null : null, + portraitTextureKey: this.portrait?.visible ? this.portrait.texture.key : null }; } @@ -239,11 +255,13 @@ export class StoryScene extends Phaser.Scene { this.applyBackground(page.background); this.chapterText?.setText(page.chapter); - if (page.portrait) { + const portraitKey = this.pagePortraitKey(page); + + if (portraitKey) { this.portraitFrame?.setVisible(true); this.portrait?.setVisible(true); this.portraitDivider?.setVisible(true); - this.portrait?.setTexture(portraitKeys[page.portrait]); + this.portrait?.setTexture(portraitKeys[portraitKey]); this.portrait?.setDisplaySize(128, 128); this.nameText?.setPosition(270, this.scale.height - 210); this.bodyText?.setPosition(270, this.scale.height - 160); @@ -262,6 +280,10 @@ export class StoryScene extends Phaser.Scene { this.progressText?.setText(`${this.pageIndex + 1} / ${this.pages.length} SPACE / ENTER`); } + private pagePortraitKey(page: StoryPage) { + return page.portrait ?? (page.speaker ? portraitBySpeaker[page.speaker] : undefined); + } + private applyBackground(textureKey: string) { if (!this.background) { return;