arena/agent.md

259 lines
26 KiB
Markdown

# Update: Special Projectile Trail
- Special projectile movement can leave short-lived visual afterimages controlled by `SPECIAL_EFFECT.PROJECTILE.TRAIL`.
- Trail sprites copy the projectile's current texture frame, scale, rotation, and flip state, then fade out without affecting hit detection or damage.
- Trail density and cost are bounded by `TRAIL.INTERVAL_MS` and `TRAIL.LIFETIME_MS`.
# Update: Split Special Projectile Visual Configs
- Special projectile visual asset settings are split by caster type: melee visuals live under `SPECIAL_EFFECT.MELEE`, and ranged visuals live under `SPECIAL_EFFECT.RANGE`.
- `SPECIAL_EFFECT.PROJECTILE` now owns shared movement, hit-detection, and trail tuning only, such as acceleration, travel duration, hold time, target area, arena clamp, hit radius, lifetime, and afterimages.
# Update: One-Shot Accelerating Special Projectile
- Melee special sprites now use `SPECIAL_EFFECT.MELEE.REPEAT = 0`, so the configured sprite sheet plays once instead of looping while the projectile travels.
- `special-melee-effect-1` has its `frameSequence` commented out for now, restoring the natural sprite-sheet order. A commented example remains next to the asset for quick tuning later.
- Special projectile movement now uses a tween instead of constant `physics.moveTo`. `SPECIAL_EFFECT.PROJECTILE.startHoldMs` controls the stationary pre-launch tell, `travelDurationMs` controls launch speed when set, and `movementEase` controls the acceleration curve; `speed` remains the fallback if `travelDurationMs` is unset.
# Update: Special Effect Frame Sequence Refresh
- Special effect animations now compare the existing Phaser animation against the current configured frames, repeat count, and frame rate. If the config changed, the old global animation key is removed and recreated so `frameSequence` edits take effect without stale animation data.
- `frameSequence` remains 1-based for sprite-sheet inspection, then converts through `generateFrameNumbers(..., { frames })`, preserving repeated frames when a special asset enables a custom sequence.
# Update: Special Effect Frame Rate And Render Budget
- Special effect sprite animations now multiply their configured `frameRate` by `SPECIAL_EFFECT.FRAME_RATE_MULTIPLIER`. This changes only visual frame playback; caster hold time, launch delay, projectile speed, travel distance, and timers keep their existing progress speed.
- The special focus blur snapshot is skipped when the living fighter count is above `SPECIAL_EFFECT.FOCUS_LAYER.BLUR_MAX_FIGHTERS`. The dim layer and raised caster focus still render, avoiding the expensive full-arena render-texture blur during larger battles.
- Special projectile hit checks now use the per-frame combat spatial index to inspect only fighters near the projectile segment when the index is available, while preserving the full-array fallback.
# Update: Elite Kill Splash
- Elite fighters now trigger a kill splash when they directly kill an enemy. The splash is centered on the killed fighter's body position.
- Splash damage is `COMBAT.ELITE_KILL_SPLASH_DAMAGE_PERCENT` of the killed fighter's max HP and applies to living enemy fighters inside `COMBAT.ELITE_KILL_SPLASH_RADIUS`.
- Splash kills still use the normal kill/death flow for logs, death statistics, despawn, split-on-death, scoreboard, and match-finish checks. `COMBAT.ELITE_KILL_SPLASH_CHAIN_ENABLED` is `false` by default, so splash kills do not recursively trigger more splashes unless explicitly enabled.
- A short team-colored pixel-dot burst is rendered for the splash when supplemental combat effects are enabled, avoiding smooth vector circles.
# Update: Team Card Focus Toggle
- Team cards in the left HUD still select a random living fighter from the clicked team and zoom the camera in.
- Clicking the same already-focused team card again clears the selected fighter, requests `CAMERA.MIN_ZOOM`, restores the match status summary, and removes the focused team-card state. If automatic spectator focus is active, that camera mode immediately reapplies its own zoom; this is intended.
# Update: Special Effect Projectile
- Special battle effects are implemented separately from meteor/frost barrages in `src/game/combat/specialEffects.js`.
- Tuning lives under `SPECIAL_EFFECT` in `src/constants.js`; the same object is also exposed as `WORLD_EFFECT.SPECIAL` for world-effect domain access.
- Each live match schedules one special-effect attempt at a random time between `SPECIAL_EFFECT.TRIGGER_DELAY_MIN_MS` and `TRIGGER_DELAY_MAX_MS`. It retries briefly only when no eligible caster exists, and never fires more than once per match.
- Eligible casters are living, non-elite, non-magic fighters from teams that are not currently tied for first by represented living count. `SPECIAL_EFFECT.CASTER.BALANCE_NON_MAGIC_TYPES` first balances between available non-magic caster types, then picks a fighter inside that type, so ranged casters are not drowned out by the larger melee roster. The caster holds Hurt frame index `1` long enough for the zoom/focus layer to read, then the attack animation launches a giant projectile.
- The caster receives realtime special invulnerability for `SPECIAL_EFFECT.CASTER.INVULNERABLE_MS`. Normal attacks, world-effect damage/frost survivor effects, and special instant kills all skip fighters whose invulnerability window is still active.
- During that Hurt-frame preparation hold, combat is frozen by pausing fighter AI, Arcade Physics, and the scene clock, so combat/world timers do not advance. A realtime cinematic timer releases the pause when the attack motion begins.
- While the caster holds the Hurt frame, `SPECIAL_EFFECT.CASTER_SPARKLE` plays only frames 2, 3, and 4 from `public/assets/effects/special/effect.png` above the caster's eye area, then removes the sparkle before the attack motion begins.
- The caster is emphasized with a temporary focus stack: a blurred render-texture snapshot of the battlefield, a dim layer, then the caster and special launch/projectile effects above that layer. `SPECIAL_EFFECT.FOCUS_LAYER` controls depths, blur, alpha, and fades.
- `SPECIAL_EFFECT.CAMERA.CENTER_ON_CASTER_AT_START` makes the camera center on the caster location immediately when the special cast begins, before the slower zoom/focus motion continues.
- When the special projectile starts moving, the special camera stops zooming in and zooms out in place through `SPECIAL_EFFECT.CAMERA.PROJECTILE_VIEW_ZOOM` and `PROJECTILE_ZOOM_OUT_MS`; it does not follow the projectile.
- Special melee sprites can use explicit 1-based `frameSequence` arrays, but `special-melee-effect-1` currently leaves the sequence disabled to play the sheet once in natural order. The projectile uses `startHoldMs` to remain visible near the caster before traveling.
- At target-selection time, the projectile locks onto the densest enemy tile area using represented `stackCount` population. `SPECIAL_EFFECT.PROJECTILE.targetAreaTiles` controls that scan footprint.
- The moving special projectile visual is selected by caster type: melee casters fire one random sprite from `SPECIAL_EFFECT.MELEE.ASSETS`, while ranged casters use `SPECIAL_EFFECT.RANGE`. Projectile movement uses a tweened Arcade Physics sprite so acceleration can be tuned while path hit checks still run per update. Projectile travel is clamped by `SPECIAL_EFFECT.PROJECTILE.arenaEdgePadding`, and any living fighter intersecting the projectile path is killed instantly, with kill/death records flowing through the normal combat cleanup path.
- Special preparation pauses existing combat objects as well as fighters: active battle tweens, world-effect fall tweens, combat-object animations, physics velocities, scene time, and Arcade Physics are restored only after the realtime Hurt-frame hold finishes.
# Update: Large-Battle Render Budget
- When the user-entered total fighter count is greater than `PERFORMANCE.LARGE_BATTLE_FIGHTER_THRESHOLD`, match setup enforces `PERFORMANCE.LARGE_BATTLE_RENDERED_FIGHTER_LIMIT` as a hard budget for physically rendered fighter plans.
- Randomized compression still rolls blocks first. If the resulting rendered count exceeds the budget, failed normal 100-member blocks and normal remainder groups are promoted to elite groups until the rendered count is within the limit or no promotable groups remain.
- `stackCount` is preserved, so team totals, death statistics, spectator weighting, and dense-area targeting continue to use the represented population.
# Update: Field HUD Text Removal
- Zoom-visible fighter HUD slots no longer create battlefield name text. Team identity is carried by the team-colored sprite shadow, so selected and zoom-visible fighters only borrow health-bar HUD objects.
# Update: Large-Battle Elite Probability
- When the user-entered total fighter count is greater than `PERFORMANCE.LARGE_BATTLE_FIGHTER_THRESHOLD`, randomized elite compression uses `FIGHTER.ELITE.RANDOMIZED_COMPRESSION.LARGE_BATTLE_ELITE_BLOCK_PROBABILITY` instead of the normal block probability.
- The large-battle elite probability is `0.8` by default and is clamped against the normal probability so large battles never use a lower elite block ratio than regular randomized compression.
# Update: Elite Magic Attack Effect Scale
- Instant-spell attack effects use `FIGHTER.ATTACK_EFFECT_SCALE_MULTIPLIER` for their normal visual scale.
- Elite magic fighters multiply that normal spell-effect scale by `FIGHTER.ELITE.ATTACK_EFFECT_SCALE_MULTIPLIER`, so giant elite casters can have attack effects sized independently from fighter body scale.
- Elite spawn plans select skins only from the configured `FIGHTER.ELITE.TYPE` list (`melee`, `magic`); normal plans retain the full skin pool.
# Update: Elite Stacking Compression
- Below `FIGHTER.ELITE.RANDOMIZED_COMPRESSION.MIN_TEAM_SIZE`, complete `FIGHTER.ELITE.STACK_SIZE = 100` blocks use fixed elite compression; with the current threshold of `100`, entries containing a complete block use randomized compression instead.
- At or above the randomized-compression threshold, each complete 100-member block becomes one elite with probability `ELITE_BLOCK_PROBABILITY = 0.6`; a non-elite block remains 100 rendered normal fighters. When the total requested fighter count is above the large-battle threshold, the probability increases to `LARGE_BATTLE_ELITE_BLOCK_PROBABILITY = 0.8`.
- Elite fighters use nested `FIGHTER.ELITE` settings for their type, 5x visual scale, magic attack-effect scale, HP ratio, attack range, attack damage, and attack/movement speed formulas. A bonus multiplier of `0` disables that added elite bonus; `1` applies the configured stack exponent fully.
- Critical hits and world effects distinguish elite targets: elites take max-HP based critical/meteor/frost damage (10%/40%/20%), while normal fighters take 2x critical hit damage and the existing fixed meteor/frost damage.
- `COMBAT.KILL_REWARD_ENABLED` is `false` for elite-compressed battles. Kills still update logs and death statistics, but no fighter heals, grows, or gains attack/movement speed from a kill.
- Team cards display living physical composition as `E : elite count | N : normal count`. Death statistics, spectator thresholds/centers, and dense-area world-effect targeting continue to use represented `stackCount` population.
- Elite representative fighters do not expand Slime `spawnMultiplier` or `splitOnDeath`; applying a randomly selected per-unit trait to an aggregated army would duplicate the represented population.
# Update: Focused Combat Effects In Large Battles
- When the live fighter count reaches `PERFORMANCE.LARGE_BATTLE_FIGHTER_THRESHOLD`, supplemental combat visuals are suppressed unless a meteor camera focus is active.
- Large-battle suppression covers critical-hit labels, instant-spell attack sprites, kill-heal sprites, and kill-growth tweens; damage outcomes remain unchanged. Kill healing/growth is now disabled by the elite-compression policy.
- Meteor/frost world-effect visuals remain visible because they establish the temporary camera focus. Projectile visuals remain active because their current objects also perform hit detection.
# Update: Dense-Area Meteor Barrage
- Fire and frost world effects now target the `WORLD_EFFECT.AREA_TILES` tile square containing the highest living-fighter density instead of a random fighter location.
- Each activation renders that large warning area, then drops `WORLD_EFFECT.IMPACT_COUNT_MIN` to `IMPACT_COUNT_MAX` smaller strikes within it. Only the smaller impact zones apply damage, frost, and lingering slow areas.
- `WORLD_EFFECT.WARNING_DURATION_MS` tunes how long the large targeting warning remains visible. `IMPACT_AREA_TILES`, `IMPACT_STAGGER_MS`, and `IMPACT_VISUAL_SCALE` tune the barrage footprint, rhythm, and sprite size, while `SIZE_SCALE_VARIANCE` randomizes individual impact scale.
- `WORLD_EFFECT.INTERVAL` delays the first barrage from match start; `WORLD_EFFECT.REPEAT_INTERVAL` controls later normal barrages, while sudden-death repetition continues to use `SUDDEN_DEATH.INTERVAL_MS`.
- Meteor screen shake scales from the same size multiplier, with base values in `WORLD_EFFECT.METEOR_SHAKE_DURATION_MS` and `WORLD_EFFECT.METEOR_SHAKE_INTENSITY`.
# Update: Direct Fighter Counts And Spawn Zones
- Live match entries interpret a suffix such as `Alice*250` as that team's assigned fighter count; entries without a suffix receive one assigned fighter.
- The former team-size inputs are removed. Presentation mode retains its fixed preview size through suffixed internal entries.
- `SPAWN.MAX_FIGHTER_COUNT` caps only fighters assigned through participant input. Slime `spawnMultiplier` and `splitOnDeath` additions are game traits and are not counted against that input cap.
- Match-start validation shows a styled fighter-cap warning card beneath the participant nickname input, emphasizes requested and allowed counts separately, and clears when names are edited or a valid match is submitted.
- For starting-zone placement, `SPAWN.FIGHTERS_PER_STARTING_ZONE` defines how many assigned fighters share each team zone.
# Update: Large Battle Performance
- Combat target acquisition now builds a per-frame spatial grid so every fighter that needs a fresh target can search nearby cells instead of scanning the full battlefield array.
- Large battle thresholds and related tuning live in `PERFORMANCE` inside `src/constants.js`, including target grid size, HUD pool size, minimap dot size, and large-battle corpse despawn delay.
- Fighter health HUD objects are pooled. Fighters no longer own permanent HUD objects, and selected or zoom-visible nearby fighters borrow health-bar slots without battlefield name text.
- The minimap is separated from the field camera. During live matches, `ArenaScene` draws a lightweight graphics minimap through a dedicated `minimap-hud` camera while the main camera ignores the minimap object and the HUD camera ignores field objects. Presentation/waiting mode hides the minimap.
- Dead fighter despawn switches to the large-battle delay when the current fighter count reaches `PERFORMANCE.LARGE_BATTLE_FIGHTER_THRESHOLD`.
# Update: Dead Fighter Despawn
- Dead fighters now keep their initial opacity at death, then fade out over `FIGHTER.DEAD_DESPAWN_DELAY_MS` before being removed.
- Adjust the corpse lifetime in `src/constants.js` by changing `FIGHTER.DEAD_DESPAWN_DELAY_MS`; adjust the final fade target with `FIGHTER.DEAD_DESPAWN_ALPHA`.
- Despawn uses the Phaser scene timer and a matching tween so pause/state cleanup follows the existing match lifecycle.
# Update: Team Shadow Rendering
- Team color is now represented by recoloring the built-in floor shadow pixels on each fighter spritesheet instead of rendering a duplicated `teamMarker` sprite.
- `fighterAssets.js` owns lazy team-shadow texture and animation generation for actual `skin + action + teamColor` combinations. Avoid pre-generating every team/skin/action combination because that can move the bottleneck into startup texture creation and memory use.
- `fighterFactory.js` should keep each fighter to one Phaser sprite. Name labels and health bars remain separate HUD objects, but there is no per-fighter team marker sprite to synchronize.
- `combat.js` must resolve action animations through `ensureFighterTeamAnimation()` so action changes keep the team-colored shadow.
- Frost stun uses body tint only. Do not use tint for persistent team identity.
# Agent: Arena Picker
## 0. 필수
- 작업이 완료되면 작업에 관련된 모든 문서를 업데이트한다
## 1. 프로젝트 정의
**Arena Picker**는 Phaser 3 게임 엔진과 Vite 번들러를 기반으로 구축된 **대규모 팀 전투 시뮬레이션 웹 애플리케이션**입니다. 사용자가 입력한 여러 명의 참가자(닉네임)를 바탕으로 각 참가자를 하나의 팀으로 설정하고, 지정된 인원만큼의 캐릭터를 생성하여 자동 전투를 시뮬레이션합니다.
서버 런타임은 Fastify를 사용하며, MongoDB 커넥션 풀을 유지해 유니크 방문자 수와 전투 사망 통계를 기록하는 간단한 통계 API를 제공합니다.
## 2. 프로젝트 전체 구조 (Directory Tree)
```text
├── index.html # 메인 HTML 진입점 및 UI 레이아웃
├── package.json # 프로젝트 의존성 및 스크립트 정의 (Phaser, Vite, Fastify, MongoDB)
├── config.json # 로컬 서버/MongoDB 설정 (git ignore)
├── config.json.sample # 공유용 서버/MongoDB 설정 예시
├── agent.md # 프로젝트 개요 및 기능 정의 (본 문서)
├── CONTEXT.md # 상세 개발 가이드 및 로직 설명
├── todo.md # 작업 내역 및 잔여 이슈 관리
├── server/ # Fastify API 서버 및 MongoDB 연결 관리
│ ├── index.js # Fastify 서버 진입점, Vite 개발 미들웨어, 정적 배포 서빙
│ ├── config.js # config.json 로드 및 MongoDB URI 조립
│ ├── db.js # MongoClient 커넥션 풀 생성/재사용/종료
│ ├── deathStats.js # 전투 종료 시 오늘 일자별 종족 사망 통계 누적 API
│ ├── about.js # About 개발자정보/개인정보처리방침 기본값 시드 및 조회 API
│ └── visitors.js # 유니크 방문자 체크 및 통계 API
├── public/ # 정적 리소스 (게임 에셋)
│ └── assets/
│ ├── effects/ # 공통 전투/월드 이펙트 스프라이트시트
│ │ ├── heal/ # 처치 회복 연출
│ │ ├── world_Effect.png # 화염 메테오 7프레임 이미지
│ │ └── world_Effect_2.png # 냉기 메테오 7프레임 이미지
│ └── characters/ # 20종 이상의 캐릭터 스킨 및 투사체 에셋
│ ├── archer/, armored-axeman/, armored-orc/, ... (중략)
│ └── wizard/ # 각 폴더 내 애니메이션 시트 및 이펙트 포함
└── src/ # 소스 코드 root
├── main.js # Phaser 게임 인스턴스 생성, 옵션 drawer/재시작/일시정지 UI 제어
├── constants.js # 전역 물리/UI 상수 통합 관리 (공격력, 체력, 줌, 카메라 속도 등)
├── styles.css # UI 스타일링 (인트로, 옵션 drawer, 좌측 HUD 레일, 좌측 하단 킬로그, 상단 전투 안내바)
├── game/ # 게임 로직 모듈 (역할별 하위 폴더 구성)
│ ├── arena/ # 아레나 및 씬 관리
│ │ ├── ArenaScene.js # 메인 게임 씬 (Orchestrator, 생명주기 및 모듈 조율)
│ │ ├── arenaRenderer.js# 경기장 바닥, 격자 및 팀 시작 영역 렌더링
│ │ └── arenaSpectatorCamera.js # 지능형 관전 카메라 및 줌 로직
│ ├── combat/ # 전투 시스템
│ │ ├── combat.js # 전투 AI, 투사체 및 피격 판정 핵심 엔진
│ │ ├── combatSettings.js # 전투 속도 및 이동 배율 관리
│ │ ├── arenaFinalCombatEffects.js # 최종 교전 슬로우 모션 등 연출 효과
│ │ └── worldEffects.js # 주기적 메테오/냉각지대 및 냉기 동결 효과
│ ├── fighter/ # 캐릭터 및 에셋
│ │ ├── fighterAssets.js # 스프라이트 로드 및 팀 실루엣 동적 생성
│ │ ├── fighterFactory.js # 캐릭터 인스턴스화 및 HUD 동기화
│ │ ├── fighterManifest.js # 20종 캐릭터 스탯/특성 상세 정의
│ │ ├── fighterStats.js # 근접/원거리/마법 프로필 판별 및 스탯 해석
│ │ └── fighterSelection.js # 캐릭터 스킨 무작위 선택 로직
├── match/ # 매치 및 진행
│ ├── matchSetup.js # 팀 구성(닉네임 배수 파싱 포함) 및 스폰 좌표 계산 (스타팅 영역/랜덤)
│ └── arenaMatchRuntime.js # 매치 진행 중 헬퍼 (스폰 클러스터, 팀 크기 동기화)
...
## 7. 주요 기능 상세 (New)
### 7.1 닉네임 배수 시스템 (Multi-Spawn)
- 사용자가 닉네임 뒤에 `*N` (예: `홍길동*2`)을 입력하면 해당 팀은 기본 팀 인원의 N배만큼 생성됩니다.
- 스타팅 존 모드에서 배수만큼의 독립된 스폰 지점이 할당되어 전략적인 분산 배치가 이루어집니다.
- 닉네임 표시 시 `*N` 접미사는 자동으로 제거되어 깔끔한 UI를 유지합니다.
### 7.2 서든 데스 (Sudden Death) 시스템
- 매치 시작 후 일정 시간(기본 8초)이 경과하면 전장의 환경이 극도로 위험해지는 서든 데스 상태에 진입합니다.
- 메테오 생성 주기가 비약적으로 단축(기본 1초)되며, 빙결 효과를 가진 냉기 메테오가 집중 투하됩니다.
- `constants.js`를 통해 활성화 여부, 시작 시간, 주기 등을 간편하게 조정할 수 있습니다.
### 7.3 밀집 구역 기반 월드 이펙트 포격
- 월드 이펙트는 랜덤 생존자 대신 `WORLD_EFFECT.AREA_TILES` 크기 범위 중 현재 생존 캐릭터가 가장 많이 모인 위치를 표적으로 선택합니다.
- 선택 범위를 먼저 경고로 표시한 뒤, 그 내부에 작은 화염 또는 냉기 메테오를 3~4발 분산 투하합니다.
- 피해, 기절, 냉각 감속은 큰 경고 범위 전체가 아니라 각각의 작은 탄착 영역에만 적용됩니다.
└── ui/ # UI 컴포넌트 및 API 연동
├── arenaKillLog.js # [New] 독립된 킬로그 DOM 조작 모듈
├── arenaScoreboard.js # [New] 팀 스코어 badge 업데이트 모듈
├── battleDeathNotice.js # [New] 상단 사망 공지 메시지 및 UI 관리
├── victoryCelebration.js # [New] 승리 축하 연출 (DOM/Audio) 모듈
├── matchForm.js # 설정 폼 제어 및 localStorage 유지
├── aboutDialog.js # About 다이얼로그, 개발자정보/개인정보처리방침 표시
├── deathStats.js # 사망 통계 API 호출 래퍼
└── visitorCounter.js # 방문자 체크 API 호출 및 표시
```
## 3. 상세 기술 가이드 (Context Routing)
토큰 절약 및 효율적인 정보 조회를 위해 상세 로직은 기능별로 분리되어 보관됩니다. 특정 모듈 작업 시 아래의 관련 문서를 먼저 읽으십시오.
- **[인프라 및 전역 설정] [context/core.md](./context/core.md)**: `main.js`, `constants.js`, 개발/유지보수 공통 규칙.
- **[서버 및 API] [context/server.md](./context/server.md)**: Fastify 서버, MongoDB 연동, 방문자 및 사망 통계 API 상세.
- **[아레나 및 카메라] [context/arena.md](./context/arena.md)**: `ArenaScene` 오케스트레이션, 지능형 카메라 추적, 미니맵 가이드라인.
- **[전투 엔진] [context/combat.md](./context/combat.md)**: 전투 AI, 엘리트 피해 판정, 비활성화된 처치 보너스 경로, 슬로우모션 및 월드 이펙트 연출.
- **[캐릭터 및 에셋] [context/fighter.md](./context/fighter.md)**: 캐릭터 공장, 동적 실루엣 생성, 종족 및 특성(Slime 등) 정의.
- **[매치 로직 및 UI] [context/match-ui.md](./context/match-ui.md)**: 팀 구성 및 스폰 알고리즘, HUD 레이아웃, 킬로그, 승리 연출 UI.
- **[스타일 및 디자인] [context/style.md](./context/style.md)**: CSS 모듈 구조, 디자인 변수, 반응형 및 애니메이션 가이드.
## 4. 기술 사양
- **Framework**: Phaser 3.90.0 (Arcade Physics 기반)
- **Build Tool**: Vite 7.1.12
- **Server**: Fastify 5.x (`@fastify/static`, `@fastify/middie`)
- **Database**: MongoDB 7.x Node Driver
- **UI Logic**: Vanilla JS & CSS (Flexbox/Grid 활용)
## 5. 서버/API 설정
- 개발/운영 서버는 `npm run dev` 또는 `npm start`로 실행하며 기본 포트는 `config.json``SERVER_PORT` 값인 `9736`입니다.
- `config.json`은 로컬 설정 파일이므로 저장소에 커밋하지 않습니다. 새 환경에서는 `config.json.sample`을 복사해 사용합니다.
- 기본 API:
- `GET /api/health`: 서버 및 MongoDB 설정 여부 확인.
- `POST /api/visitors/check`: 현재 브라우저 방문자를 체크하고 유니크 방문자 수를 반환.
- `GET /api/visitors/stats`: 전체 유니크 방문자 수 조회.
- `GET /api/about`: 데이터베이스에서 실시간으로 개발자정보와 개인정보처리방침 Markdown 조회 (캐시 없이 즉시 반영).
- `GET /api/death-stats/today`: 오늘의 종족별 전투 사망 통계 조회.
- `POST /api/death-stats/today`: 종료된 전투의 종족별 사망 수를 오늘 집계에 누적.
## 6. 관련 문서
- [CONTEXT.md](./CONTEXT.md): 상세 개발 가이드 및 핵심 로직 설명 (필독)
- [todo.md](./todo.md): 작업 내역 및 잔여 이슈 관리