Improve battle status effect visibility

This commit is contained in:
2026-07-03 04:25:46 +09:00
parent a2ec898791
commit 302d0e5f2d

View File

@@ -1398,6 +1398,14 @@ type UnitEffectSummary = {
label: string;
value: string;
tone: number;
turns?: number;
};
type PreviewModifierSummary = {
icon: BattleUiIconKey;
label: string;
value: string;
tone: number;
};
type SavedUnitState = Pick<UnitData, 'id' | 'level' | 'exp' | 'hp' | 'maxHp' | 'attack' | 'move' | 'x' | 'y'> & {
@@ -5691,67 +5699,101 @@ export class BattleScene extends Phaser.Scene {
return parts.join(' / ') || '보정 없음';
}
private battleBuffBonusText(buff: BattleBuffState) {
private battleBuffShortText(buff: BattleBuffState) {
const parts = [
buff.attackBonus > 0 ? `+${buff.attackBonus}` : '',
buff.hitBonus > 0 ? `+${buff.hitBonus}` : '',
buff.criticalBonus > 0 ? `+${buff.criticalBonus}` : ''
buff.attackBonus > 0 ? `공+${buff.attackBonus}` : '',
buff.hitBonus > 0 ? `명+${buff.hitBonus}` : '',
buff.criticalBonus > 0 ? `치+${buff.criticalBonus}` : ''
].filter(Boolean);
return `${parts.join(' / ') || '효과 유지'} · ${buff.turns}`;
return `${parts.join(' ') || '유지'} · ${buff.turns}`;
}
private previewModifierLabels(preview: CombatPreview) {
const labels: string[] = [];
private previewModifierSummaries(preview: CombatPreview): PreviewModifierSummary[] {
const summaries: PreviewModifierSummary[] = [];
const buff = this.battleBuffs.get(preview.attacker.id);
const equipmentBonusCount = [
preview.equipmentDamageBonus,
preview.equipmentDamageReduction,
preview.equipmentHitBonus,
preview.equipmentCriticalBonus
].filter((bonus) => bonus > 0).length;
const equipmentText = this.equipmentPreviewBonusText(preview);
const terrainParts = [
preview.terrainDefenseBonus > 0 ? `방+${preview.terrainDefenseBonus}` : '',
preview.terrainHitPenalty > 0 ? `명-${preview.terrainHitPenalty}` : ''
].filter(Boolean);
if (buff) {
labels.push(`${buff.label} ${this.battleBuffBonusText(buff)}`);
summaries.push({
icon: 'focus',
label: buff.label,
value: this.battleBuffShortText(buff),
tone: palette.gold
});
}
if (preview.bondDamageBonus > 0 && preview.bondLabel) {
labels.push(`공명 피해 +${preview.bondDamageBonus}%`);
summaries.push({
icon: 'leadership',
label: '공명',
value: `피해 +${preview.bondDamageBonus}%`,
tone: 0x83d6ff
});
}
if (equipmentBonusCount > 0) {
labels.push(`장비 보정 ${equipmentBonusCount}`);
if (equipmentText !== '보정 없음') {
summaries.push({
icon: 'mastery',
label: '장비',
value: equipmentText,
tone: 0xd8b15f
});
}
if (preview.terrainDefenseBonus > 0) {
labels.push(`${preview.terrainLabel} 방어 +${preview.terrainDefenseBonus}`);
}
if (preview.terrainHitPenalty > 0) {
labels.push(`지형 명중 -${preview.terrainHitPenalty}`);
if (terrainParts.length > 0) {
summaries.push({
icon: 'terrain',
label: preview.terrainLabel,
value: terrainParts.join(' / '),
tone: 0x59d18c
});
}
return labels.length > 0 ? labels : ['추가 보정 없음'];
return summaries;
}
private supportPreviewModifierLabels(preview: SupportPreview) {
private supportPreviewModifierSummaries(preview: SupportPreview): PreviewModifierSummary[] {
if (preview.usable.effect === 'heal') {
const baseBonus = preview.healAmount - preview.usable.power;
return [
`${this.usableChannelLabel(preview.usable)} 회복 ${preview.healAmount}`,
baseBonus > 0 ? `능력/장비 +${baseBonus}` : '추가 보정 없음'
{
icon: this.usablePowerIcon(preview.usable),
label: this.usableChannelLabel(preview.usable),
value: `회복 +${preview.healAmount}`,
tone: 0x59d18c
},
{
icon: preview.usable.command === 'strategy' ? 'intelligence' : 'accessory',
label: baseBonus > 0 ? '보정' : '기본',
value: baseBonus > 0 ? `+${baseBonus}` : '추가 없음',
tone: baseBonus > 0 ? palette.gold : 0x647485
}
];
}
return [
`공격 +${preview.attackBonus}`,
`명중 +${preview.hitBonus}`,
`치명 +${preview.criticalBonus}`,
`${preview.duration}턴 유지`
{
icon: 'focus',
label: preview.usable.name,
value: `공+${preview.attackBonus} 명+${preview.hitBonus}`,
tone: palette.gold
},
{
icon: 'critical',
label: '치명',
value: `+${preview.criticalBonus}`,
tone: 0xd8732c
},
{
icon: 'leadership',
label: '지속',
value: `${preview.duration}`,
tone: 0x83d6ff
}
];
}
private compactModifierText(labels: string[], limit = 2, maxLength = 34) {
const text = labels.slice(0, limit).join(' · ');
const suffix = labels.length > limit ? ` · 외 ${labels.length - limit}` : '';
return this.truncateUiText(`${text}${suffix}`, maxLength);
}
private truncateUiText(text: string, maxLength: number) {
return text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 3))}...` : text;
}
@@ -5798,7 +5840,7 @@ export class BattleScene extends Phaser.Scene {
const { panelX, panelWidth } = this.layout;
const left = panelX + 24;
const width = panelWidth - 48;
const height = locked ? 396 : 360;
const height = locked ? 438 : 402;
const y = Math.max(this.sideContentTop(), Math.min(this.sideContentTop() + 328, this.sideContentBottom(10) - height));
const accent = this.previewAccentColor(preview);
const defenderClass = getUnitClass(preview.defender.classKey);
@@ -5809,7 +5851,7 @@ export class BattleScene extends Phaser.Scene {
const sourceLabel = preview.usable ? this.usableChannelLabel(preview.usable) : attackerEquipmentLabel;
const sourceName = preview.usable?.name ?? attackerWeapon.name;
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 0.98));
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 1));
bg.setOrigin(0);
bg.setStrokeStyle(2, accent, 0.78);
@@ -5859,16 +5901,9 @@ export class BattleScene extends Phaser.Scene {
);
this.renderPreviewForecastRow(preview, left + 10, y + 264, width - 20, accent);
this.renderPreviewModifierStrip(
left + 10,
y + 300,
width - 20,
'mastery',
this.compactModifierText(this.previewModifierLabels(preview), 2, 36),
accent
);
this.renderPreviewModifierCards(left + 10, y + 300, width - 20, this.previewModifierSummaries(preview), accent);
const badgeY = y + 328;
const badgeY = y + 370;
const maxBadgeRight = left + width - 10;
let badgeX = left + 10;
badgeX = this.renderPreviewBadge(badgeX, badgeY, this.actionIcon(preview), this.previewActionTypeLabel(preview), maxBadgeRight);
@@ -5881,7 +5916,7 @@ export class BattleScene extends Phaser.Scene {
this.renderPreviewBadge(detailBadgeX, badgeY, 'mastery', '장비 보정', maxBadgeRight);
}
if (locked) {
this.renderPreviewConfirmBar(left + 10, y + 360, width - 20, accent, `${preview.defender.name} 선택됨`);
this.renderPreviewConfirmBar(left + 10, y + 402, width - 20, accent, `${preview.defender.name} 선택됨`);
}
}
@@ -5889,11 +5924,11 @@ export class BattleScene extends Phaser.Scene {
const { panelX, panelWidth } = this.layout;
const left = panelX + 24;
const width = panelWidth - 48;
const height = locked ? 394 : 360;
const height = locked ? 438 : 402;
const y = Math.max(this.sideContentTop(), Math.min(this.sideContentTop() + 344, this.sideContentBottom(10) - height));
const accent = this.usableAccentColor(preview.usable);
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 0.98));
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 1));
bg.setOrigin(0);
bg.setStrokeStyle(2, accent, 0.78);
@@ -5966,16 +6001,9 @@ export class BattleScene extends Phaser.Scene {
}
this.renderSupportForecastRow(preview, left + 10, y + 264, width - 20, accent);
this.renderPreviewModifierStrip(
left + 10,
y + 300,
width - 20,
preview.usable.effect === 'heal' ? this.usablePowerIcon(preview.usable) : 'leadership',
this.compactModifierText(this.supportPreviewModifierLabels(preview)),
accent
);
this.renderPreviewModifierCards(left + 10, y + 300, width - 20, this.supportPreviewModifierSummaries(preview), accent);
const badgeY = y + 328;
const badgeY = y + 370;
const maxBadgeRight = left + width - 10;
let badgeX = left + 10;
badgeX = this.renderPreviewBadge(badgeX, badgeY, this.usableIcon(preview.usable), this.usableEffectLabel(preview.usable), maxBadgeRight);
@@ -5985,7 +6013,72 @@ export class BattleScene extends Phaser.Scene {
this.renderPreviewBadge(detailBadgeX, badgeY, 'leadership', `${preview.duration}`, maxBadgeRight);
}
if (locked) {
this.renderPreviewConfirmBar(left + 10, y + 360, width - 20, accent, `${preview.target.name} 선택됨`);
this.renderPreviewConfirmBar(left + 10, y + 402, width - 20, accent, `${preview.target.name} 선택됨`);
}
}
private renderPreviewModifierCards(
x: number,
y: number,
width: number,
summaries: PreviewModifierSummary[],
accent: number
) {
const visible = summaries.slice(0, 3);
if (visible.length === 0) {
const row = this.trackSideObject(this.add.rectangle(x, y, width, 58, 0x0b1118, 0.94));
row.setOrigin(0);
row.setStrokeStyle(1, accent, 0.44);
this.trackSideIcon(x + 24, y + 29, 'success', 34);
this.trackSideObject(this.add.text(x + 50, y + 10, '보정 출처', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: '#9fb0bf',
fontStyle: '700'
}));
this.trackSideObject(this.add.text(x + 50, y + 30, '추가 보정 없음', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '14px',
color: '#d4dce6',
fontStyle: '700'
}));
return;
}
const gap = 6;
const cardWidth = (width - gap * 2) / 3;
visible.forEach((summary, index) => {
const cardX = x + index * (cardWidth + gap);
const card = this.trackSideObject(this.add.rectangle(cardX, y, cardWidth, 58, 0x0b1118, 0.96));
card.setOrigin(0);
card.setStrokeStyle(1, summary.tone, 0.62);
const iconFrame = this.trackSideObject(this.add.rectangle(cardX + 21, y + 29, 38, 38, 0x16212d, 0.92));
iconFrame.setStrokeStyle(1, summary.tone, 0.5);
this.trackSideIcon(cardX + 21, y + 29, summary.icon, 34);
this.trackSideObject(this.add.text(cardX + 44, y + 7, summary.label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '10px',
color: '#9fb0bf',
fontStyle: '700',
fixedWidth: cardWidth - 52
}));
this.trackSideObject(this.add.text(cardX + 44, y + 24, this.truncateUiText(summary.value, 14), {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: '#f2e3bf',
fontStyle: '700',
fixedWidth: cardWidth - 52
}));
});
if (summaries.length > visible.length) {
const extraText = this.trackSideObject(this.add.text(x + width - 8, y + 4, `+${summaries.length - visible.length}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
color: '#f4dfad',
fontStyle: '700'
}));
extraText.setOrigin(1, 0);
}
}
@@ -6239,26 +6332,6 @@ export class BattleScene extends Phaser.Scene {
buffText.setOrigin(1, 0);
}
private renderPreviewModifierStrip(x: number, y: number, width: number, icon: BattleUiIconKey, text: string, accent: number) {
const row = this.trackSideObject(this.add.rectangle(x, y, width, 24, 0x101820, 0.96));
row.setOrigin(0);
row.setStrokeStyle(1, accent, 0.42);
this.trackSideIcon(x + 15, y + 12, icon, 20);
this.trackSideObject(this.add.text(x + 32, y + 6, '보정', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
color: '#9fb0bf',
fontStyle: '700'
}));
this.trackSideObject(this.add.text(x + 68, y + 5, text, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: text === '추가 보정 없음' ? '#aeb7c2' : '#f2e3bf',
fontStyle: '700',
fixedWidth: width - 76
}));
}
private renderPreviewBadge(x: number, y: number, icon: BattleUiIconKey, label: string, maxRight?: number) {
const width = Phaser.Math.Clamp(label.length * 7 + 48, 76, 146);
if (maxRight && x + width > maxRight) {
@@ -10822,7 +10895,7 @@ export class BattleScene extends Phaser.Scene {
}
this.turnNumber += 1;
this.tickBattleBuffs();
const expiredBuffMessage = this.tickBattleBuffs();
this.activeFaction = 'ally';
this.fastForwardHeld = false;
this.actedUnitIds.clear();
@@ -10834,13 +10907,16 @@ export class BattleScene extends Phaser.Scene {
this.updateObjectiveTracker();
this.checkBattleEvents();
this.centerCameraOnAllyLine();
const turnMessage = [`${this.turnNumber}턴 아군 차례입니다.`, recoveryMessage, '행동할 장수를 선택하세요.'].filter(Boolean).join('\n');
const turnMessage = [`${this.turnNumber}턴 아군 차례입니다.`, expiredBuffMessage, recoveryMessage, '행동할 장수를 선택하세요.'].filter(Boolean).join('\n');
this.renderRosterPanel('ally', turnMessage);
const firstUnit = this.firstActionableAlly();
if (firstUnit) {
this.renderSituationPanel(`${this.turnNumber}턴 아군 차례입니다.\n다음 행동: ${firstUnit.name}`);
this.focusActionableAlly(firstUnit, [`${this.turnNumber}턴 아군 차례입니다.`, recoveryMessage, '이동할 파란 칸을 선택하세요.'].filter(Boolean).join('\n'));
this.renderSituationPanel([`${this.turnNumber}턴 아군 차례입니다.`, expiredBuffMessage, recoveryMessage, `다음 행동: ${firstUnit.name}`].filter(Boolean).join('\n'));
this.focusActionableAlly(
firstUnit,
[`${this.turnNumber}턴 아군 차례입니다.`, expiredBuffMessage, recoveryMessage, '이동할 파란 칸을 선택하세요.'].filter(Boolean).join('\n')
);
}
}
@@ -11326,12 +11402,22 @@ export class BattleScene extends Phaser.Scene {
}
private tickBattleBuffs() {
const expired: string[] = [];
this.battleBuffs.forEach((buff, unitId) => {
buff.turns -= 1;
if (buff.turns <= 0) {
this.battleBuffs.delete(unitId);
expired.push(`${this.unitName(unitId)} ${buff.label}`);
}
});
if (expired.length === 0) {
return undefined;
}
const message = `상태효과 만료\n${expired.slice(0, 3).join('\n')}${expired.length > 3 ? `\n외 ${expired.length - 3}` : ''}`;
this.pushBattleLog(message);
return message;
}
private cancelAttackTargeting() {
@@ -12629,19 +12715,21 @@ export class BattleScene extends Phaser.Scene {
`${this.directionSymbol(direction)} ${this.unitDirectionLabel(direction)}`
);
const statTop = top + 164;
const effects = this.unitEffectSummaries(unit);
const hasEffects = effects.length > 0;
const statTop = top + (hasEffects ? 154 : 164);
const statGap = hasEffects ? 21 : 24;
statLabels.forEach((stat, index) => {
this.renderStatRow(stat.key, stat.label, unit.stats[stat.key], left, statTop + index * 24, width);
this.renderStatRow(stat.key, stat.label, unit.stats[stat.key], left, statTop + index * statGap, width);
});
const effects = this.unitEffectSummaries(unit);
const effectText = effects.length > 0 ? ` / 효과 ${this.compactUnitEffectText(effects)}` : '';
const positionText = `위치 ${unit.x + 1}, ${unit.y + 1} · ${terrainRule.label} ${terrainRating}% · 이동 ${unit.move}${acted ? ' · 행동완료' : ''}${effectText}`;
const statusPanel = this.trackSideObject(this.add.rectangle(left, top + 288, width, 32, 0x101820, 0.9));
const positionText = `위치 ${unit.x + 1}, ${unit.y + 1} · ${terrainRule.label} ${terrainRating}% · 이동 ${unit.move}${acted ? ' · 행동완료' : ''}`;
const statusTop = top + (hasEffects ? 264 : 288);
const statusPanel = this.trackSideObject(this.add.rectangle(left, statusTop, width, 32, 0x101820, 0.9));
statusPanel.setOrigin(0);
statusPanel.setStrokeStyle(1, 0x40515e, 0.5);
this.trackSideIcon(left + 17, top + 304, effects[0]?.icon ?? (acted ? 'counter' : this.unitMoveIcon(unit)), 25);
this.trackSideObject(this.add.text(left + 36, top + 294, positionText, {
this.trackSideIcon(left + 17, statusTop + 16, acted ? 'counter' : this.unitMoveIcon(unit), 25);
this.trackSideObject(this.add.text(left + 36, statusTop + 6, positionText, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: acted ? '#bdbdbd' : '#9fb0bf',
@@ -12649,8 +12737,12 @@ export class BattleScene extends Phaser.Scene {
lineSpacing: 2
}));
const equipmentTop = top + 326;
this.renderEquipmentSummary(unit, left, equipmentTop, width);
const effectTop = top + (hasEffects ? 302 : 326);
const equipmentTop = hasEffects ? top + 350 : effectTop;
if (hasEffects) {
this.renderUnitEffectCards(effects, left, effectTop, width);
}
this.renderEquipmentSummary(unit, left, equipmentTop, width, hasEffects);
if (message) {
this.renderPanelMessage(message, left, equipmentTop + 156, width, 42);
}
@@ -12668,10 +12760,11 @@ export class BattleScene extends Phaser.Scene {
if (buff) {
effects.push({
icon: 'leadership',
icon: 'focus',
label: buff.label,
value: this.battleBuffBonusText(buff),
tone: palette.gold
value: this.battleBuffShortText(buff),
tone: palette.gold,
turns: buff.turns
});
}
if (turnStartParts.length > 0) {
@@ -12686,80 +12779,127 @@ export class BattleScene extends Phaser.Scene {
return effects;
}
private compactUnitEffectText(effects: UnitEffectSummary[]) {
return this.compactModifierText(
effects.map((effect) => `${effect.label}: ${effect.value}`),
2
);
private renderUnitEffectCards(effects: UnitEffectSummary[], x: number, y: number, width: number) {
const visible = effects.slice(0, 2);
const gap = 6;
const cardWidth = (width - gap) / 2;
visible.forEach((effect, index) => {
const cardX = x + index * (cardWidth + gap);
const bg = this.trackSideObject(this.add.rectangle(cardX, y, cardWidth, 42, 0x101820, 0.97));
bg.setOrigin(0);
bg.setStrokeStyle(1, effect.tone, 0.62);
const iconFrame = this.trackSideObject(this.add.rectangle(cardX + 20, y + 21, 32, 32, 0x0a0f14, 0.92));
iconFrame.setStrokeStyle(1, effect.tone, 0.56);
this.trackSideIcon(cardX + 20, y + 21, effect.icon, 30);
this.trackSideObject(this.add.text(cardX + 42, y + 4, effect.label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '10px',
color: '#9fb0bf',
fontStyle: '700',
fixedWidth: cardWidth - 52
}));
this.trackSideObject(this.add.text(cardX + 42, y + 20, this.truncateUiText(effect.value, 15), {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: '#f2e3bf',
fontStyle: '700',
fixedWidth: cardWidth - 52
}));
if (effect.turns) {
const badge = this.trackSideObject(this.add.rectangle(cardX + cardWidth - 18, y + 14, 26, 18, 0x211624, 0.96));
badge.setStrokeStyle(1, effect.tone, 0.78);
const turnText = this.trackSideObject(this.add.text(cardX + cardWidth - 18, y + 14, `${effect.turns}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
color: '#fff2b8',
fontStyle: '700'
}));
turnText.setOrigin(0.5);
}
});
if (effects.length > visible.length) {
const extraText = this.trackSideObject(this.add.text(x + width - 8, y + 4, `+${effects.length - visible.length}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
color: '#f4dfad',
fontStyle: '700'
}));
extraText.setOrigin(1, 0);
}
}
private renderEquipmentSummary(unit: UnitData, x: number, y: number, width: number) {
this.trackSideIcon(x + 15, y + 12, 'mastery', 30);
private renderEquipmentSummary(unit: UnitData, x: number, y: number, width: number, compact = false) {
this.trackSideIcon(x + 15, y + 12, 'mastery', compact ? 25 : 30);
this.trackSideObject(this.add.text(x + 32, y, '장비 / 숙련도', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '17px',
fontSize: compact ? '14px' : '17px',
color: '#f2e3bf',
fontStyle: '700'
}));
equipmentSlots.forEach((slot, index) => {
this.renderEquipmentRow(unit, slot, x, y + 28 + index * 40, width);
const rowTop = y + (compact ? 20 : 28) + index * (compact ? 30 : 40);
this.renderEquipmentRow(unit, slot, x, rowTop, width, compact);
});
}
private renderEquipmentRow(unit: UnitData, slot: EquipmentSlot, x: number, y: number, width: number) {
private renderEquipmentRow(unit: UnitData, slot: EquipmentSlot, x: number, y: number, width: number, compact = false) {
const state = unit.equipment[slot];
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, 38, isTreasure ? 0x1f2430 : 0x101820, isTreasure ? 0.98 : 0.95));
const rowHeight = compact ? 28 : 38;
const bg = this.trackSideObject(this.add.rectangle(x, y, width, rowHeight, isTreasure ? 0x1f2430 : 0x101820, isTreasure ? 0.98 : 0.95));
bg.setOrigin(0);
bg.setStrokeStyle(1, isTreasure ? palette.gold : 0x53606c, isTreasure ? 0.66 : 0.5);
const iconFrame = this.trackSideObject(this.add.rectangle(x + 23, y + 19, 36, 36, 0x0a0f14, 0.92));
const iconSize = compact ? 26 : 36;
const iconFrame = this.trackSideObject(this.add.rectangle(x + (compact ? 19 : 21), y + rowHeight / 2, iconSize, iconSize, 0x0a0f14, 0.92));
iconFrame.setStrokeStyle(1, isTreasure ? palette.gold : 0x53606c, isTreasure ? 0.78 : 0.54);
this.trackSideIcon(x + 23, y + 19, this.itemIcon(item, slot), 34);
this.trackSideIcon(x + (compact ? 19 : 21), y + rowHeight / 2, this.itemIcon(item, slot), compact ? 24 : 34);
this.trackSideObject(this.add.text(x + 48, y + 3, equipmentSlotLabels[slot], {
this.trackSideObject(this.add.text(x + 40, y + (compact ? 1 : 3), equipmentSlotLabels[slot], {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
fontSize: compact ? '10px' : '11px',
color: '#9fb0bf',
fontStyle: '700'
}));
const bonusText = this.itemBonusText(item);
this.trackSideObject(this.add.text(x + 86, y + 3, bonusText, {
this.trackSideObject(this.add.text(x + 78, y + (compact ? 1 : 3), bonusText, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
fontSize: compact ? '10px' : '11px',
color: '#aeb7c2',
fixedWidth: width - 170
}));
this.trackSideObject(this.add.text(x + 48, y + 17, item.name, {
this.trackSideObject(this.add.text(x + 40, y + (compact ? 12 : 17), item.name, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '15px',
fontSize: compact ? '12px' : '15px',
color: isTreasure ? '#f4dfad' : '#d4dce6',
fontStyle: '700',
fixedWidth: width - 150
}));
const levelText = this.trackSideObject(this.add.text(x + width - 10, y + 5, `Lv ${state.level}`, {
const levelText = this.trackSideObject(this.add.text(x + width - 10, y + (compact ? 2 : 5), `Lv ${state.level}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '15px',
fontSize: compact ? '12px' : '15px',
color: isTreasure ? '#f2e3bf' : '#d4dce6',
fontStyle: '700'
}));
levelText.setOrigin(1, 0);
this.trackSideIcon(x + width - 76, y + 27, 'mastery', 20);
const expText = this.trackSideObject(this.add.text(x + width - 10, y + 24, `${state.exp}/${next}`, {
this.trackSideIcon(x + width - 76, y + (compact ? 20 : 27), 'mastery', compact ? 16 : 20);
const expText = this.trackSideObject(this.add.text(x + width - 10, y + (compact ? 17 : 24), `${state.exp}/${next}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
fontSize: compact ? '10px' : '11px',
color: '#c8d2dd',
fontStyle: '700'
}));
expText.setOrigin(1, 0);
this.drawGauge(x + 48, y + 32, width - 132, 5, state.exp / next, isTreasure ? 0xd8b15f : 0x58aee0);
this.drawGauge(x + 40, y + (compact ? 24 : 32), width - 124, compact ? 4 : 5, state.exp / next, isTreasure ? 0xd8b15f : 0x58aee0);
}
private createBattleUiIcon(x: number, y: number, icon: BattleUiIconKey, size = 22) {