# 프로젝트 개요

작성일: 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` 등록 여부를 반드시 확인해야 합니다.
