feat: upgrade combat and item icons

This commit is contained in:
2026-07-17 21:25:57 +09:00
parent e92535165d
commit eb41c05f60
19 changed files with 437 additions and 42 deletions

View File

@@ -1,7 +1,7 @@
import Phaser from 'phaser';
import storyFirstSortieUrl from '../../assets/images/story/05-first-sortie.png';
import { soundDirector } from '../audio/SoundDirector';
import { battleUiIconFrames, battleUiIconTextureKey, loadBattleUiIcons, type BattleUiIconKey } from '../data/battleUiIcons';
import { battleUiIconFrames, battleUiIconTextureForSize, loadBattleUiIcons, type BattleUiIconKey } from '../data/battleUiIcons';
import { selectCampSkin, type CampSkinSelection } from '../data/campSkins';
import {
equipmentExpToNext,
@@ -280,9 +280,11 @@ type SortiePortraitRosterLayout = {
nextEnabled: boolean;
};
type CampSupplyId = 'bean' | 'wine' | 'salve';
type CampSupplyDefinition = {
label: string;
usableId: string;
usableId: CampSupplyId;
title: string;
description: string;
healHp: number;
@@ -290,6 +292,7 @@ type CampSupplyDefinition = {
};
type MerchantItemDefinition = {
usableId: CampSupplyId;
label: string;
title: string;
description: string;
@@ -780,18 +783,21 @@ const campSupplyByLabel = new Map(campSupplies.map((supply) => [supply.label, su
const merchantItems: MerchantItemDefinition[] = [
{
usableId: 'bean',
label: '콩',
title: '콩',
description: '값싸고 가벼운 회복 도구입니다.',
price: 40
},
{
usableId: 'wine',
label: '탁주',
title: '탁주',
description: '병력 회복과 작은 공명 상승에 유용합니다.',
price: 70
},
{
usableId: 'salve',
label: '상처약',
title: '상처약',
description: '큰 부상을 빠르게 회복합니다.',
@@ -15880,18 +15886,33 @@ export class CampScene extends Phaser.Scene {
}
private renderSortieBattleUiIcon(iconKey: BattleUiIconKey, x: number, y: number, size: number, depth: number, alpha = 1) {
if (!this.textures.exists(battleUiIconTextureKey)) {
const displaySize = this.campUiLength(size);
const textureKey = battleUiIconTextureForSize(displaySize);
if (!this.textures.exists(textureKey)) {
const fallback = this.trackSortie(this.add.circle(x, y, size / 2, palette.gold, 0.18));
fallback.setDepth(depth);
return;
}
const icon = this.trackSortie(this.add.image(x, y, battleUiIconTextureKey, battleUiIconFrames[iconKey]));
icon.setDisplaySize(this.campUiLength(size), this.campUiLength(size));
const icon = this.trackSortie(this.add.image(x, y, textureKey, battleUiIconFrames[iconKey]));
icon.setDisplaySize(displaySize, displaySize);
icon.setAlpha(alpha);
icon.setDepth(depth);
}
private renderCampBattleUiIcon(iconKey: BattleUiIconKey, x: number, y: number, size: number, alpha = 1) {
const displaySize = this.campUiLength(size);
const textureKey = battleUiIconTextureForSize(displaySize);
if (!this.textures.exists(textureKey)) {
return this.track(this.add.circle(x, y, size / 2, palette.gold, 0.18));
}
const icon = this.track(this.add.image(x, y, textureKey, battleUiIconFrames[iconKey]));
icon.setDisplaySize(displaySize, displaySize);
icon.setAlpha(alpha);
return icon;
}
private unitClassIconKey(unit: UnitData): BattleUiIconKey {
switch (unit.classKey) {
case 'lord':
@@ -16024,9 +16045,10 @@ export class CampScene extends Phaser.Scene {
bg.setOrigin(0);
bg.setDepth(depth);
bg.setStrokeStyle(1, assigned > 0 ? palette.green : enabled ? palette.gold : 0x53606c, assigned > 0 ? 0.78 : enabled ? 0.5 : 0.28);
this.renderSortieBattleUiIcon(supply.usableId, buttonX + 10, y + 10, 14, depth + 1, enabled ? 0.96 : 0.48);
this.trackSortie(
this.add.text(
buttonX + (buttonWidth - 5) / 2,
buttonX + (buttonWidth - 5) / 2 + 6,
y + 5,
`${supply.title} ${assigned}`,
this.textStyle(9, assigned > 0 ? '#a8ffd0' : enabled ? '#c8d2dd' : '#77818c', true)
@@ -21583,8 +21605,9 @@ export class CampScene extends Phaser.Scene {
const bg = this.track(this.add.rectangle(x, y, width, 40, canBuy ? 0x151f2a : 0x111820, canBuy ? 0.94 : 0.76));
bg.setOrigin(0);
bg.setStrokeStyle(1, canBuy ? palette.gold : 0x53606c, canBuy ? 0.58 : 0.36);
this.track(this.add.text(x + 10, y + 6, item.title, this.textStyle(13, canBuy ? '#f2e3bf' : '#7f8994', true)));
this.track(this.add.text(x + 10, y + 23, `${item.price}`, this.textStyle(11, canBuy ? '#d8b15f' : '#7f8994', true)));
this.renderCampBattleUiIcon(item.usableId, x + 20, y + 20, 26, canBuy ? 1 : 0.5);
this.track(this.add.text(x + 40, y + 6, item.title, this.textStyle(13, canBuy ? '#f2e3bf' : '#7f8994', true)));
this.track(this.add.text(x + 40, y + 23, `${item.price}`, this.textStyle(11, canBuy ? '#d8b15f' : '#7f8994', true)));
const button = this.track(this.add.rectangle(x + width - 42, y + 20, 60, 26, canBuy ? 0x1a2630 : 0x121922, canBuy ? 0.96 : 0.72));
button.setStrokeStyle(1, canBuy ? palette.gold : 0x53606c, canBuy ? 0.72 : 0.4);
@@ -21669,11 +21692,12 @@ export class CampScene extends Phaser.Scene {
bg.setOrigin(0);
bg.setStrokeStyle(1, enabled ? palette.gold : 0x53606c, enabled ? 0.62 : 0.38);
this.track(this.add.text(x + 14, y + 10, `${supply.title} x${amount}`, this.textStyle(16, amount > 0 ? '#f2e3bf' : '#7f8994', true)));
this.renderCampBattleUiIcon(supply.usableId, x + 28, y + 31, 36, amount > 0 ? 1 : 0.46);
this.track(this.add.text(x + 56, y + 10, `${supply.title} x${amount}`, this.textStyle(16, amount > 0 ? '#f2e3bf' : '#7f8994', true)));
this.track(
this.add.text(x + 14, y + 34, supply.description, {
this.add.text(x + 56, y + 34, supply.description, {
...this.textStyle(12, enabled ? '#c8d2dd' : '#77818c'),
wordWrap: { width: width - 128, useAdvancedWrap: true }
wordWrap: { width: width - 170, useAdvancedWrap: true }
})
);
@@ -22075,8 +22099,8 @@ export class CampScene extends Phaser.Scene {
private normalizedSortieItemAssignments(assignments?: CampaignSortieItemAssignments) {
const selected = new Set(this.selectedSortieUnitIds);
const validSupplyIds = new Set(campSupplies.map((supply) => supply.usableId));
const remainingBySupply = new Map(campSupplies.map((supply) => [supply.usableId, this.inventoryAmount(supply.label)]));
const validSupplyIds = new Set<string>(campSupplies.map((supply) => supply.usableId));
const remainingBySupply = new Map<string, number>(campSupplies.map((supply) => [supply.usableId, this.inventoryAmount(supply.label)]));
return Object.entries(assignments ?? {}).reduce<CampaignSortieItemAssignments>((next, [unitId, stocks]) => {
if (!selected.has(unitId)) {