diff --git a/src/constants.js b/src/constants.js index 3f57b4d..973592d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -164,6 +164,10 @@ export const UI = { SELECTED_FIGHTER_OUTLINE_GREEN: 228, SELECTED_FIGHTER_OUTLINE_BLUE: 64, SELECTED_FIGHTER_OUTLINE_ALPHA: 0.65, + // 상단 종족별 사망 통계 공지 설정 + BATTLE_NOTICE_DELAY_MS: 5000, + BATTLE_NOTICE_VISIBLE_MS: 2000, + BATTLE_NOTICE_INTERVAL_MS: 10000, }; // 9. TEAM 도메인 diff --git a/src/game/arena/ArenaScene.js b/src/game/arena/ArenaScene.js index 0fd32d0..2185a92 100644 --- a/src/game/arena/ArenaScene.js +++ b/src/game/arena/ArenaScene.js @@ -51,9 +51,6 @@ import { import { updateScoreboard } from "../../ui/arenaScoreboard.js"; import { appendKillLog, resetKillLog } from "../../ui/arenaKillLog.js"; import { - BATTLE_NOTICE_DELAY_MS, - BATTLE_NOTICE_INTERVAL_MS, - BATTLE_NOTICE_VISIBLE_MS, clearBattleNotice, showBattleDeathNotice, } from "../../ui/battleDeathNotice.js"; @@ -295,7 +292,7 @@ export class ArenaScene extends Phaser.Scene { }); } - scheduleBattleNotice(delayMs = BATTLE_NOTICE_DELAY_MS) { + scheduleBattleNotice(delayMs = UI.BATTLE_NOTICE_DELAY_MS) { this.battleNoticeTimer?.remove(false); this.battleNoticeTimer = this.time.delayedCall(delayMs, () => { this.battleNoticeTimer = null; @@ -339,12 +336,12 @@ export class ArenaScene extends Phaser.Scene { showBattleDeathNotice(noticeNode, message); this.battleNoticeHideTimer?.remove(false); - this.battleNoticeHideTimer = this.time.delayedCall(BATTLE_NOTICE_VISIBLE_MS, () => { + this.battleNoticeHideTimer = this.time.delayedCall(UI.BATTLE_NOTICE_VISIBLE_MS, () => { this.battleNoticeHideTimer = null; clearBattleNotice(noticeNode); if (!this.matchOver && !this.presentationMode) { - this.scheduleBattleNotice(BATTLE_NOTICE_INTERVAL_MS); + this.scheduleBattleNotice(UI.BATTLE_NOTICE_INTERVAL_MS); } }); } diff --git a/src/styles/game-ui.css b/src/styles/game-ui.css index e489c49..bede3f1 100644 --- a/src/styles/game-ui.css +++ b/src/styles/game-ui.css @@ -124,11 +124,12 @@ display: flex; align-items: center; justify-content: center; - width: min(420px, 72vmin, calc(100vw - 64px)); + width: auto; + max-width: min(640px, calc(100vw - 40px)); min-height: 38px; border: 1px solid rgb(238 185 73 / 0.26); border-radius: 8px; - padding: 8px 14px; + padding: 8px 18px; background: rgb(8 10 7 / 0.68); color: #ffe8b4; font-size: 0.8rem; @@ -136,6 +137,7 @@ line-height: 1.35; text-align: center; text-shadow: 1px 1px 2px #000; + white-space: nowrap; opacity: 0; pointer-events: none; transform: translate(-50%, -10px); diff --git a/src/ui/battleDeathNotice.js b/src/ui/battleDeathNotice.js index 850b604..e3c1e28 100644 --- a/src/ui/battleDeathNotice.js +++ b/src/ui/battleDeathNotice.js @@ -1,3 +1,5 @@ +import { UI } from "../constants.js"; + const SPECIES_KEYS = ["human", "orc", "skeleton", "slime", "wolf", "bear"]; const SPECIES_LABELS = { bear: "곰", @@ -22,9 +24,12 @@ const DEATH_NOTICE_TEMPLATES = [ "{species}{particle} 전투 중 {count}명 쓰러졌습니다. 관중석은 침착한 척하는 중입니다.", ]; -export const BATTLE_NOTICE_DELAY_MS = 5000; -export const BATTLE_NOTICE_VISIBLE_MS = 2000; -export const BATTLE_NOTICE_INTERVAL_MS = 10000; +const SYSTEM_TIP_TEMPLATES = [ + "경보: 화염 메테오는 낙하 지점 5x5 영역에 강력한 폭발 피해를 입힙니다!", + "주의: 냉기 메테오는 피해와 함께 2초간 동결 및 냉각을 유발합니다.", + "팁: 근접 캐릭터는 20% 확률로 치명타를 터뜨려 적을 즉사시킵니다.", + "성장: 적 처치 시 체력을 30% 회복하며, 크기와 속도가 최대 5배까지 커집니다.", +]; export function createDeathCounts() { return SPECIES_KEYS.reduce((counts, species) => { @@ -42,7 +47,8 @@ export function normalizeDeathCounts(value = {}) { export function addDeathCounts(baseCounts, matchCounts) { return SPECIES_KEYS.reduce((counts, species) => { - counts[species] = (baseCounts?.[species] ?? 0) + (matchCounts?.[species] ?? 0); + counts[species] = + (baseCounts?.[species] ?? 0) + (matchCounts?.[species] ?? 0); return counts; }, {}); } @@ -52,17 +58,26 @@ export function normalizeSpecies(value) { } export function createDeathNoticeMessage(deathsBySpecies, seed = 0) { - const topSpecies = SPECIES_KEYS - .map((species) => ({ species, count: deathsBySpecies?.[species] ?? 0 })) - .sort((left, right) => right.count - left.count)[0]; + // 3번에 한 번꼴로 시스템 팁 출력 + if (seed % 3 === 0) { + return SYSTEM_TIP_TEMPLATES[ + Math.floor(seed / 3) % SYSTEM_TIP_TEMPLATES.length + ]; + } + + const topSpecies = SPECIES_KEYS.map((species) => ({ + species, + count: deathsBySpecies?.[species] ?? 0, + })).sort((left, right) => right.count - left.count)[0]; if (!topSpecies || topSpecies.count === 0) { return "오늘 사망자 집계는 아직 0명입니다. 이 평화가 얼마나 버틸까요?"; } - const template = DEATH_NOTICE_TEMPLATES[ - (topSpecies.count + seed) % DEATH_NOTICE_TEMPLATES.length - ]; + const template = + DEATH_NOTICE_TEMPLATES[ + (topSpecies.count + seed) % DEATH_NOTICE_TEMPLATES.length + ]; return template .replace("{species}", SPECIES_LABELS[topSpecies.species]) diff --git a/todo.md b/todo.md index ca258b7..b946d17 100644 --- a/todo.md +++ b/todo.md @@ -260,4 +260,9 @@ - 새로운 CSS 모듈 구조와 디자인 원칙을 설명하는 `context/style.md` 문서를 신규 생성. - `agent.md`의 상세 기술 가이드(Context Routing) 섹션에 스타일 및 디자인 항목을 추가하여 문서 접근성 개선. +42. 상단 공지(Battle Notice) 콘텐츠 확장 (완료) +- **조치 사항**: + - 사망 통계만 보여주던 공지 UI에 게임 시스템 가이드(화염/냉기 메테오 특성, 밀리 치명타 확률 등) 팁을 추가. + - 사망 통계 공지 2회당 1회의 비율로 시스템 팁이 교차 출력되도록 로직 개선. +