diff --git a/src/game/data/battleItems.ts b/src/game/data/battleItems.ts index 04ffd6d..d046a78 100644 --- a/src/game/data/battleItems.ts +++ b/src/game/data/battleItems.ts @@ -42,28 +42,28 @@ export const itemCatalog: Record = { }, 'twin-oath-blades': { id: 'twin-oath-blades', - name: '쌍의검', + name: '자웅일대검', slot: 'weapon', rank: 'treasure', - description: '도원에서 맹세한 뜻을 담아 벼린 쌍검.', + description: '유비가 쌍으로 사용하는 보검. 자검과 웅검이 한 쌍을 이룬다.', attackBonus: 3, effects: ['공명 관계가 70 이상이면 공격 경험치 +2'] }, 'green-dragon-glaive': { id: 'green-dragon-glaive', - name: '청룡월도', + name: '청룡언월도', slot: 'weapon', rank: 'treasure', - description: '묵직한 월도로 전열을 갈라내는 관우의 보물 무기.', + description: '관우의 상징과도 같은 대도. 푸른 용의 기세로 전열을 가른다.', attackBonus: 5, effects: ['적보다 무력이 높으면 피해 보너스'] }, 'serpent-spear': { id: 'serpent-spear', - name: '장팔장창', + name: '장팔사모', slot: 'weapon', rank: 'treasure', - description: '한 번 내지르면 기세까지 꿰뚫는 장비의 긴 창.', + description: '장비가 휘두르는 긴 사모. 거친 돌진과 호통에 잘 어울린다.', attackBonus: 5, effects: ['연속 공격 판정 보정'] }, diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index 240f546..714c803 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -1930,19 +1930,25 @@ export class BattleScene extends Phaser.Scene { const item = getItem(state.itemId); const next = equipmentExpToNext(state.level); const isTreasure = item.rank === 'treasure'; - const bg = this.trackSideObject(this.add.rectangle(x, y, width, 24, isTreasure ? 0x1f2430 : 0x101820, isTreasure ? 0.94 : 0.86)); + const bg = this.trackSideObject(this.add.rectangle(x, y, width, 26, isTreasure ? 0x1f2430 : 0x101820, isTreasure ? 0.94 : 0.86)); bg.setOrigin(0); bg.setStrokeStyle(1, isTreasure ? palette.gold : 0x53606c, isTreasure ? 0.58 : 0.42); - this.trackSideObject(this.add.text(x + 8, y + 5, equipmentSlotLabels[slot], { + const iconFrame = this.trackSideObject(this.add.rectangle(x + 14, y + 13, 22, 22, 0x0a0f14, 0.82)); + iconFrame.setStrokeStyle(1, isTreasure ? palette.gold : 0x53606c, isTreasure ? 0.72 : 0.48); + + const icon = this.trackSideObject(this.add.image(x + 14, y + 13, this.itemIconKey(item.id))); + icon.setDisplaySize(22, 22); + + this.trackSideObject(this.add.text(x + 31, y + 6, equipmentSlotLabels[slot], { fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif', fontSize: '12px', color: '#9fb0bf', fontStyle: '700' })); - this.trackSideObject(this.add.text(x + 54, y + 4, item.name, { + this.trackSideObject(this.add.text(x + 78, y + 4, item.name, { fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif', - fontSize: '14px', + fontSize: '13px', color: isTreasure ? '#f4dfad' : '#d4dce6', fontStyle: isTreasure ? '700' : '400' })); @@ -1969,7 +1975,12 @@ export class BattleScene extends Phaser.Scene { })); levelText.setOrigin(1, 0); - this.drawGauge(x + 54, y + 19, width - 154, 4, state.exp / next, isTreasure ? 0xd8b15f : 0x58aee0); + this.drawGauge(x + 78, y + 20, width - 178, 4, state.exp / next, isTreasure ? 0xd8b15f : 0x58aee0); + } + + private itemIconKey(itemId: string) { + const key = `item-${itemId}`; + return this.textures.exists(key) ? key : 'item-training-sword'; } private itemBonusText(item: ReturnType) { diff --git a/src/game/scenes/BootScene.ts b/src/game/scenes/BootScene.ts index 09f07cb..ac9db42 100644 --- a/src/game/scenes/BootScene.ts +++ b/src/game/scenes/BootScene.ts @@ -67,6 +67,7 @@ export class BootScene extends Phaser.Scene { create() { this.createPetalTexture(); + this.createEquipmentIconTextures(); this.createUnitAnimations(); this.scene.start('TitleScene'); } @@ -81,6 +82,230 @@ export class BootScene extends Phaser.Scene { graphics.destroy(); } + private createEquipmentIconTextures() { + this.createItemIcon('item-training-sword', (graphics) => this.drawSwordIcon(graphics, 0xbfd6e6, 0x6b4a2c)); + this.createItemIcon('item-twin-oath-blades', (graphics) => { + this.drawSwordIcon(graphics, 0xe8edf7, 0x8a5a32, -4); + this.drawSwordIcon(graphics, 0xd8b15f, 0x8a5a32, 4); + }); + this.createItemIcon('item-green-dragon-glaive', (graphics) => this.drawPolearmIcon(graphics, 0x5fbf8f, 0xd8b15f)); + this.createItemIcon('item-serpent-spear', (graphics) => this.drawSpearIcon(graphics, 0xe0e8ef, 0xb86b55)); + this.createItemIcon('item-yellow-turban-saber', (graphics) => this.drawSaberIcon(graphics, 0xd8b15f, 0x9c6b2f)); + this.createItemIcon('item-short-bow', (graphics) => this.drawBowIcon(graphics, 0xc58a4c, 0xe8dfca)); + this.createItemIcon('item-leader-axe', (graphics) => this.drawAxeIcon(graphics, 0xd8dfe6, 0x8d5a3a)); + + this.createItemIcon('item-cloth-armor', (graphics) => this.drawRobeIcon(graphics, 0x91a8bc, 0xe8dfca)); + this.createItemIcon('item-lamellar-armor', (graphics) => this.drawArmorIcon(graphics, 0x7d8792, 0xd8b15f)); + this.createItemIcon('item-oath-robe', (graphics) => this.drawRobeIcon(graphics, 0x4e7aa8, 0xf0c0a8)); + this.createItemIcon('item-reinforced-lamellar', (graphics) => this.drawArmorIcon(graphics, 0xb8c4ce, 0x59a6d6)); + this.createItemIcon('item-rebel-vest', (graphics) => this.drawVestIcon(graphics, 0xb88b4a, 0xd8b15f)); + + this.createItemIcon('item-peach-charm', (graphics) => this.drawCharmIcon(graphics, 0xf0a6a4, 0xd8b15f)); + this.createItemIcon('item-war-manual', (graphics) => this.drawBookIcon(graphics, 0x5b78a0, 0xe8dfca)); + this.createItemIcon('item-bravery-token', (graphics) => this.drawTokenIcon(graphics, 0xd8b15f, 0x9b4c3a)); + this.createItemIcon('item-grain-pouch', (graphics) => this.drawPouchIcon(graphics, 0xa7794a, 0xe8dfca)); + this.createItemIcon('item-yellow-scarf-charm', (graphics) => this.drawCharmIcon(graphics, 0xd8b15f, 0x8b6f2a)); + this.createItemIcon('item-wind-quiver', (graphics) => this.drawQuiverIcon(graphics, 0x8bb3c7, 0xe8dfca)); + } + + private createItemIcon(key: string, draw: (graphics: Phaser.GameObjects.Graphics) => void) { + if (this.textures.exists(key)) { + return; + } + + const graphics = this.make.graphics({ x: 0, y: 0 }); + graphics.fillStyle(0x101820, 0); + graphics.fillRect(0, 0, 28, 28); + graphics.lineStyle(1, 0x05070a, 0.7); + draw(graphics); + graphics.generateTexture(key, 28, 28); + graphics.destroy(); + } + + private drawSwordIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number, offsetX = 0) { + graphics.lineStyle(4, bladeColor, 1); + graphics.beginPath(); + graphics.moveTo(8 + offsetX, 22); + graphics.lineTo(20 + offsetX, 6); + graphics.strokePath(); + graphics.lineStyle(2, 0xeff7ff, 0.8); + graphics.beginPath(); + graphics.moveTo(11 + offsetX, 18); + graphics.lineTo(18 + offsetX, 8); + graphics.strokePath(); + graphics.lineStyle(3, 0xd8b15f, 1); + graphics.beginPath(); + graphics.moveTo(6 + offsetX, 18); + graphics.lineTo(12 + offsetX, 23); + graphics.strokePath(); + graphics.fillStyle(gripColor, 1); + graphics.fillCircle(8 + offsetX, 23, 2); + } + + private drawPolearmIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, accentColor: number) { + graphics.lineStyle(3, 0x8d5a3a, 1); + graphics.beginPath(); + graphics.moveTo(8, 24); + graphics.lineTo(18, 5); + graphics.strokePath(); + graphics.fillStyle(bladeColor, 1); + graphics.fillTriangle(17, 4, 25, 8, 18, 15); + graphics.lineStyle(2, accentColor, 1); + graphics.beginPath(); + graphics.moveTo(16, 12); + graphics.lineTo(23, 15); + graphics.strokePath(); + } + + private drawSpearIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, accentColor: number) { + graphics.lineStyle(3, 0x8d5a3a, 1); + graphics.beginPath(); + graphics.moveTo(7, 24); + graphics.lineTo(20, 4); + graphics.strokePath(); + graphics.fillStyle(bladeColor, 1); + graphics.fillTriangle(18, 4, 24, 2, 22, 9); + graphics.fillStyle(accentColor, 1); + graphics.fillTriangle(15, 11, 20, 9, 17, 15); + } + + private drawSaberIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) { + graphics.lineStyle(4, bladeColor, 1); + graphics.beginPath(); + graphics.moveTo(8, 22); + graphics.lineTo(13, 15); + graphics.lineTo(19, 8); + graphics.lineTo(23, 6); + graphics.strokePath(); + graphics.lineStyle(3, gripColor, 1); + graphics.beginPath(); + graphics.moveTo(6, 21); + graphics.lineTo(12, 23); + graphics.strokePath(); + } + + private drawBowIcon(graphics: Phaser.GameObjects.Graphics, bowColor: number, stringColor: number) { + graphics.lineStyle(3, bowColor, 1); + graphics.beginPath(); + graphics.moveTo(20, 4); + graphics.lineTo(14, 9); + graphics.lineTo(11, 14); + graphics.lineTo(14, 19); + graphics.lineTo(20, 24); + graphics.strokePath(); + graphics.lineStyle(1, stringColor, 1); + graphics.beginPath(); + graphics.moveTo(20, 4); + graphics.lineTo(20, 24); + graphics.strokePath(); + graphics.lineStyle(2, 0xd8dfe6, 1); + graphics.beginPath(); + graphics.moveTo(7, 14); + graphics.lineTo(22, 14); + graphics.strokePath(); + } + + private drawAxeIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) { + graphics.lineStyle(3, gripColor, 1); + graphics.beginPath(); + graphics.moveTo(9, 24); + graphics.lineTo(18, 5); + graphics.strokePath(); + graphics.fillStyle(bladeColor, 1); + graphics.fillTriangle(16, 5, 25, 8, 16, 15); + graphics.fillTriangle(16, 5, 9, 9, 15, 14); + } + + private drawArmorIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillTriangle(14, 4, 24, 11, 20, 24); + graphics.fillTriangle(14, 4, 4, 11, 8, 24); + graphics.fillRect(8, 10, 12, 14); + graphics.lineStyle(2, accentColor, 0.9); + graphics.strokeRect(9, 11, 10, 12); + graphics.lineStyle(1, 0x05070a, 0.6); + graphics.beginPath(); + graphics.moveTo(10, 16); + graphics.lineTo(18, 16); + graphics.moveTo(11, 20); + graphics.lineTo(17, 20); + graphics.strokePath(); + } + + private drawRobeIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillTriangle(14, 4, 24, 24, 4, 24); + graphics.fillStyle(0x101820, 0.45); + graphics.fillTriangle(14, 8, 18, 24, 10, 24); + graphics.lineStyle(2, accentColor, 1); + graphics.beginPath(); + graphics.moveTo(14, 6); + graphics.lineTo(14, 23); + graphics.strokePath(); + } + + private drawVestIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillRect(7, 7, 14, 18); + graphics.fillStyle(0x101820, 0.7); + graphics.fillTriangle(11, 7, 17, 7, 14, 14); + graphics.lineStyle(2, accentColor, 0.9); + graphics.strokeRect(8, 8, 12, 16); + } + + private drawCharmIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillRoundedRect(8, 7, 12, 16, 2); + graphics.lineStyle(2, accentColor, 1); + graphics.strokeRoundedRect(8, 7, 12, 16, 2); + graphics.fillStyle(accentColor, 1); + graphics.fillCircle(14, 15, 3); + } + + private drawBookIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, pageColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillRect(6, 6, 16, 18); + graphics.fillStyle(pageColor, 1); + graphics.fillRect(9, 8, 10, 14); + graphics.lineStyle(1, 0x101820, 0.65); + graphics.beginPath(); + graphics.moveTo(14, 8); + graphics.lineTo(14, 22); + graphics.strokePath(); + } + + private drawTokenIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillCircle(14, 14, 9); + graphics.lineStyle(2, accentColor, 0.9); + graphics.strokeCircle(14, 14, 7); + graphics.fillStyle(accentColor, 1); + graphics.fillTriangle(14, 8, 18, 18, 10, 18); + } + + private drawPouchIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillRoundedRect(7, 10, 14, 13, 4); + graphics.fillTriangle(10, 11, 18, 11, 14, 5); + graphics.lineStyle(2, accentColor, 1); + graphics.beginPath(); + graphics.moveTo(8, 13); + graphics.lineTo(20, 13); + graphics.strokePath(); + } + + private drawQuiverIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, arrowColor: number) { + graphics.fillStyle(baseColor, 1); + graphics.fillRoundedRect(10, 8, 9, 16, 2); + graphics.lineStyle(2, arrowColor, 1); + graphics.beginPath(); + graphics.moveTo(7, 6); + graphics.lineTo(18, 20); + graphics.moveTo(13, 5); + graphics.lineTo(20, 19); + graphics.strokePath(); + } + private createUnitAnimations() { unitSheets.forEach(({ key }) => { (['south', 'east', 'north', 'west'] as UnitDirection[]).forEach((direction) => {