game/PROJECT_OVERVIEW.md

257 lines
11 KiB
Markdown

# 프로젝트 개요
작성일: 2026-04-27
## 한 줄 요약
`Colosseum's Choice`는 Flutter로 만든 콜로세움 테마의 턴제 로그라이트 전투 게임입니다. 플레이어는 전투, 상점, 휴식 스테이지를 반복하며 장비와 소모품을 얻고, 위험도 선택 기반의 공격/방어 액션으로 더 높은 스테이지를 진행합니다.
## 기본 정보
- 앱 타이틀: `Colosseum's Choice`
- Flutter 패키지명: `game_test`
- 주요 기술: Flutter, Dart, Provider, SharedPreferences
- 지원 플랫폼 폴더: `android/`, `ios/`, `web/`, `macos/`, `linux/`, `windows/`
- 주요 소스 루트: `lib/`
- 주요 데이터 루트: `assets/data/`
## 실행 흐름
1. `lib/main.dart`
- Flutter 바인딩을 초기화합니다.
- `ItemTable.load()`, `EnemyTable.load()`, `PlayerTable.load()`로 JSON 데이터를 먼저 로드합니다.
- `MultiProvider``SettingsProvider`, `ShopProvider`, `BattleProvider`를 등록합니다.
- `MainMenuScreen`을 첫 화면으로 띄웁니다.
2. 메인 메뉴
- 저장 데이터가 있으면 `Continue`가 노출됩니다.
- 이어하기는 `SaveManager.loadGame()` 결과를 `BattleProvider.loadFromSave()`로 복원합니다.
- 새 게임은 `CharacterSelectionScreen`으로 이동합니다.
3. 새 게임 시작
- `CharacterSelectionScreen`에서 전사를 선택합니다.
- `BattleProvider.initializeBattle()`로 플레이어와 1스테이지를 초기화합니다.
- `StoryScreen`을 거쳐 `MainWrapper`로 진입합니다.
4. 실제 플레이 화면
- `MainWrapper`는 하단 탭 구조입니다.
- 첫 탭은 현재 스테이지 타입에 따라 `Battle`, `Shop`, `Rest`로 표시됩니다.
- 나머지 탭은 `Inventory`, `Settings`입니다.
## 핵심 게임 루프
- 스테이지 타입은 `BattleProvider._prepareNextStage()`에서 결정됩니다.
- 우선순위는 `elite -> shop -> rest -> battle`입니다.
- 현재 설정 기준:
- 엘리트: 12스테이지마다
- 상점: 5스테이지마다
- 휴식: 8스테이지마다
- 티어 1: 1-12 스테이지
- 티어 2: 13-24 스테이지
- 티어 3: 25 스테이지 이후
전투에서 승리하면 골드와 보상 선택지가 지급되고, 보상을 선택하거나 스킵하면 체력을 일부 회복한 뒤 다음 스테이지로 넘어갑니다. 상점/휴식 스테이지는 별도 UI를 거쳐 다음 스테이지로 진행합니다.
## 전투 시스템
전투는 `BattleProvider``CombatCalculator`가 중심입니다.
- 플레이어 액션: `attack`, `defend`
- 위험도: `safe`, `normal`, `risky`
- 공격은 `totalAtk`, 방어는 `totalDefense`를 기반으로 계산됩니다.
- 위험도는 성공 확률과 효율 배율을 함께 바꿉니다.
- 운(`luck`)은 성공 확률에 더해지고 최대 100%로 제한됩니다.
- 적은 매 턴 `EnemyIntent`를 생성해 다음 행동과 위험도를 미리 보여줍니다.
현재 전투 수치 설정은 `lib/game/config/battle_config.dart`에 있습니다.
- Safe: 성공률 100%, 공격 50%, 방어 100%
- Normal: 성공률 80%, 공격 100%, 방어 200%
- Risky: 성공률 40%, 공격 200%, 방어 300%
- 적 행동 비율: 공격 70%, 방어 30%
피해 계산은 방어도와 상태 이상을 반영합니다.
- 방어도는 HP 피해보다 먼저 피해를 흡수합니다.
- `vulnerable` 상태면 받는 피해가 `GameConfig.vulnerableDamageMultiplier`만큼 증가합니다.
- 회피(`dodge`)가 성공하면 공격 피해가 들어가지 않습니다.
- 현재 방어도 감소율은 `GameConfig.armorDecayRate`로 관리됩니다.
## 상태 이상
상태 이상 타입은 `lib/game/enums.dart``StatusEffectType`에 정의되어 있습니다.
- `stun`: 해당 턴 행동 불가
- `vulnerable`: 받는 피해 증가
- `bleed`: 턴 시작 시 고정 피해
- `defenseForbidden`: 방어 액션 사용 불가
- `disarmed`: 공격력 감소
- `attackUp`: 공격력 증가 버프
아이템 효과는 `ItemEffect`로 표현되며, 공격 성공 후 `CombatCalculator.getAppliedEffects()``BattleProvider._tryApplyStatusEffects()`를 통해 대상에게 적용됩니다.
## 보상, 아이템, 장비
아이템 데이터는 `assets/data/items.json`에서 로드되고 `ItemTable`이 관리합니다.
- 슬롯: weapon, armor, shield, accessory, consumable
- 희귀도: normal, magic, rare, legendary, unique
- 티어: tier1, tier2, tier3
- 장비 스탯: 공격력, HP, 방어도, 회피, 운
- 소모품: 즉시 회복, 방어도 증가, 버프 적용 등에 사용됩니다.
아이템 생성은 `LootGenerator`가 담당합니다.
- Normal 등급은 가중치 기반 접두어로 기본 스탯 변형을 받을 수 있습니다.
- Magic 등급은 접두어를 통해 하나 이상의 스탯 보너스를 받을 수 있습니다.
- Rare 등급은 별도 이름 생성과 강한 스탯 변형을 받을 수 있습니다.
- Legendary/Unique는 기본 템플릿 성격을 유지합니다.
인벤토리는 `Character.inventory`에 보관되고, 최대 크기는 `GameConfig.maxInventorySize`입니다. 장비 장착/해제 시 최대 HP 변화로 인한 체력 악용을 막기 위해 현재 HP 비율을 유지합니다.
## 경제와 상점
상점 로직은 `ShopProvider``widgets/stage/shop_ui.dart`가 담당합니다.
- 상점 스테이지에서 장비 4개와 소모품 2개를 생성합니다.
- 아이템 구매는 골드와 인벤토리 여유 공간을 검사합니다.
- 재입고 비용은 `GameConfig.shopRerollCost`입니다.
- 판매 가격은 `item.price * GameConfig.sellPriceMultiplier`를 내림 처리합니다.
전투 승리 골드는 다음 공식으로 지급됩니다.
```text
baseGoldReward + stage * goldRewardPerStage + random(0..goldRewardVariance - 1)
```
## 저장 시스템
저장은 `lib/game/save_manager.dart`가 담당하며 `shared_preferences`를 사용합니다.
- 저장 키: `GameConfig.saveKey`
- 저장 시점: 새 스테이지 준비 시점
- 저장 내용: 스테이지, 턴 수, 플레이어 JSON, 저장 시각
- 패배 시 저장 데이터가 삭제됩니다.
현재 저장 구조는 아이템의 `id`를 중심으로 복원합니다. 따라서 런타임에 생성된 접두어, 이름, 세부 스탯 변형을 완전히 보존하려면 `Item.toJson()`/`Item.fromJson()` 형태의 상세 저장 구조가 필요합니다.
## 주요 폴더 구조
```text
lib/
main.dart 앱 진입점, 데이터 선로딩, Provider 등록
game/
model/ Character, Item, Stage, StatusEffect 등 도메인 모델
data/ JSON 로더, 아이템/적/플레이어 테이블, 이름/접두어 데이터
logic/ 전투 계산, 전리품 생성, 전투 로그 관리
config/ 게임 밸런스, 전투 수치, 테마, 문구 상수
enums.dart 게임 전반에서 쓰는 enum 정의
save_manager.dart SharedPreferences 저장/불러오기
providers/ BattleProvider, ShopProvider, SettingsProvider
screens/ 메인 메뉴, 캐릭터 선택, 스토리, 전투, 인벤토리, 설정
widgets/
battle/ 전투 UI, 애니메이션, 피드백 텍스트, 로그, 컨트롤
inventory/ 캐릭터 스탯, 장비, 인벤토리 그리드
stage/ 상점, 휴식 스테이지 UI
common/ 공통 버튼, 아이콘, 아이템 카드
utils/ 수학, 아이템, 토스트 유틸
```
```text
assets/
data/
items.json 아이템 템플릿
enemies.json 적 템플릿
players.json 플레이어 템플릿
icon/ 레거시 아이콘 데이터
images/
character/ 플레이어 캐릭터 이미지와 공격 프레임
enemies/ 적 이미지
background/ 배경 이미지
icons/ 장비, 포션, 테두리 등 UI 아이콘
```
## 화면 구성
- `MainMenuScreen`: 시작 화면, 저장 데이터 확인, 이어하기/새 게임
- `CharacterSelectionScreen`: 플레이어 템플릿 선택
- `StoryScreen`: 전투 시작 전 스토리 화면
- `MainWrapper`: 하단 탭과 현재 스테이지 화면 전환
- `BattleScreen`: 전투 UI, 이펙트 스트림 구독, 애니메이션 처리
- `InventoryScreen`: 캐릭터 능력치, 장착 장비, 인벤토리
- `SettingsScreen`: 애니메이션, 흔들림 옵션, 재시작, 메인 메뉴 복귀
## 이벤트와 UI 동기화
전투 로직과 UI 애니메이션은 이벤트 스트림으로 느슨하게 연결되어 있습니다.
- `DamageEvent`: 실제 HP 피해 텍스트 표시
- `EffectEvent`: 공격/방어/실패/회피 등 시각 효과와 충돌 타이밍 전달
- `BattleScreen`은 두 스트림을 구독하고, 애니메이션이 끝나는 시점에 `BattleProvider.handleImpact()`를 호출해 실제 피해/방어도 변화를 적용합니다.
이 구조 덕분에 계산 로직과 화면 효과가 분리되어 있지만, 턴 전환은 애니메이션 완료 콜백에 의존하는 부분이 있으므로 전투 UI 수정 시 `handleImpact()` 호출 흐름을 함께 확인해야 합니다.
## 설정과 밸런스 조정 위치
- 스테이지, 경제, 저장, 회복, 피해 배율: `lib/game/config/game_config.dart`
- 전투 성공률, 효율, 이펙트 크기/색상: `lib/game/config/battle_config.dart`
- 아이템 희귀도 가중치, 접두어 확률: `lib/game/config/item_config.dart`
- 앱 문구: `lib/game/config/app_strings.dart`
- 색상, 폰트 크기, UI 상수: `lib/game/config/theme_config.dart`
## 테스트
테스트는 `test/` 아래에 있습니다.
- 아이템 로딩과 랜덤 선택
- 아이템 희귀도/티어
- 적 데이터 로딩
- 캐릭터 장비와 HP 비율 처리
- 전투 Provider의 방어도 초기화
- 적 의도 생성/실행
- disarm 상태 효과
주요 명령:
```bash
flutter pub get
flutter test
flutter run
```
웹으로 실행하려면 보통 다음 명령을 사용합니다.
```bash
flutter run -d chrome
```
## 확장 작업 가이드
새 아이템을 추가할 때:
1. `assets/data/items.json`에 템플릿을 추가합니다.
2. 이미지가 필요하면 `assets/images/icons/...` 아래에 추가합니다.
3. 새 폴더를 추가했다면 `pubspec.yaml``flutter.assets`에 등록합니다.
4. 로딩 테스트나 랜덤 선택 테스트를 확인합니다.
새 적을 추가할 때:
1. `assets/data/enemies.json``normal` 또는 `elite`에 템플릿을 추가합니다.
2. 이미지가 필요하면 `assets/images/enemies/`에 추가합니다.
3. 티어와 장비 ID가 실제 아이템 데이터와 맞는지 확인합니다.
전투 밸런스를 조정할 때:
1. 위험도별 성공률/효율은 `BattleConfig`를 수정합니다.
2. 스테이지 보상, 상점 주기, 회복량은 `GameConfig`를 수정합니다.
3. 장비 드롭 희귀도는 `ItemConfig.defaultRarityWeights`를 수정합니다.
## 현재 눈여겨볼 점
- `pubspec.yaml`의 패키지 설명은 아직 Flutter 기본 문구입니다.
- 앱 타이틀은 `Colosseum's Choice`지만 패키지명은 `game_test`입니다.
- `StoryScreen`은 아직 플레이스홀더 이미지와 텍스트를 사용합니다.
- `initializeBattle()`에서 테스트용 아이템과 포션을 기본 지급하고 있습니다.
- 저장은 아이템 ID 중심이라 생성된 접두어/세부 스탯 보존이 제한적입니다.
- 자산 폴더를 새로 추가할 때는 `pubspec.yaml` 등록 여부를 반드시 확인해야 합니다.