game/PROJECT_OVERVIEW.md

11 KiB

프로젝트 개요

작성일: 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 데이터를 먼저 로드합니다.
    • MultiProviderSettingsProvider, 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를 거쳐 다음 스테이지로 진행합니다.

전투 시스템

전투는 BattleProviderCombatCalculator가 중심입니다.

  • 플레이어 액션: 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.dartStatusEffectType에 정의되어 있습니다.

  • 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 비율을 유지합니다.

경제와 상점

상점 로직은 ShopProviderwidgets/stage/shop_ui.dart가 담당합니다.

  • 상점 스테이지에서 장비 4개와 소모품 2개를 생성합니다.
  • 아이템 구매는 골드와 인벤토리 여유 공간을 검사합니다.
  • 재입고 비용은 GameConfig.shopRerollCost입니다.
  • 판매 가격은 item.price * GameConfig.sellPriceMultiplier를 내림 처리합니다.

전투 승리 골드는 다음 공식으로 지급됩니다.

baseGoldReward + stage * goldRewardPerStage + random(0..goldRewardVariance - 1)

저장 시스템

저장은 lib/game/save_manager.dart가 담당하며 shared_preferences를 사용합니다.

  • 저장 키: GameConfig.saveKey
  • 저장 시점: 새 스테이지 준비 시점
  • 저장 내용: 스테이지, 턴 수, 플레이어 JSON, 저장 시각
  • 패배 시 저장 데이터가 삭제됩니다.

현재 저장 구조는 아이템의 id를 중심으로 복원합니다. 따라서 런타임에 생성된 접두어, 이름, 세부 스탯 변형을 완전히 보존하려면 Item.toJson()/Item.fromJson() 형태의 상세 저장 구조가 필요합니다.

주요 폴더 구조

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/                    수학, 아이템, 토스트 유틸
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 상태 효과

주요 명령:

flutter pub get
flutter test
flutter run

웹으로 실행하려면 보통 다음 명령을 사용합니다.

flutter run -d chrome

확장 작업 가이드

새 아이템을 추가할 때:

  1. assets/data/items.json에 템플릿을 추가합니다.
  2. 이미지가 필요하면 assets/images/icons/... 아래에 추가합니다.
  3. 새 폴더를 추가했다면 pubspec.yamlflutter.assets에 등록합니다.
  4. 로딩 테스트나 랜덤 선택 테스트를 확인합니다.

새 적을 추가할 때:

  1. assets/data/enemies.jsonnormal 또는 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 등록 여부를 반드시 확인해야 합니다.