218 lines
22 KiB
Markdown
218 lines
22 KiB
Markdown
# 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.
|
|
- 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 use explicit 1-based `frameSequence` arrays, holding the largest frames longer than normal linear playback. Because melee sprites are moving projectiles, their animations loop while traveling. 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 fire `public/assets/effects/special/projectile/projectile_Effect_1.png`. Projectile movement uses an Arcade Physics sprite and `physics.moveTo`, matching the normal ranged projectile position-update path. 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): 작업 내역 및 잔여 이슈 관리
|