From 135bf2633281e45928939f89b894df1565dfef1c Mon Sep 17 00:00:00 2001 From: Horoli Date: Sun, 7 Dec 2025 22:06:51 +0900 Subject: [PATCH] Docs: Consolidate prompts 61-76 into summary and update context --- lib/game/logic/loot_generator.dart | 109 +++++++------ lib/game/model/entity.dart | 4 +- lib/providers/battle_provider.dart | 15 +- prompt/00_project_context_restore.md | 189 +++++++--------------- prompt/60_introduce_app_strings.md | 16 -- prompt/61_system_stabilization_summary.md | 27 ++++ prompt/61_update_convention_i18n.md | 11 -- prompt/62_enemy_attack_animation.md | 17 -- prompt/63_settings_enemy_anim.md | 22 --- prompt/64_fix_enemy_anim_sync.md | 14 -- prompt/65_fix_text_timing.md | 13 -- prompt/66_fix_double_animation.md | 15 -- prompt/67_revert_delay.md | 12 -- prompt/68_adjust_game_config.md | 20 --- prompt/69_sync_damage_on_impact.md | 29 ---- prompt/70_fix_player_action_crash.md | 16 -- prompt/71_fix_null_error.md | 12 -- prompt/72_delay_enemy_intent.md | 11 -- prompt/73_prevent_first_turn_defense.md | 81 ---------- prompt/74_fix_item_name_interpolation.md | 12 -- 20 files changed, 155 insertions(+), 490 deletions(-) delete mode 100644 prompt/60_introduce_app_strings.md create mode 100644 prompt/61_system_stabilization_summary.md delete mode 100644 prompt/61_update_convention_i18n.md delete mode 100644 prompt/62_enemy_attack_animation.md delete mode 100644 prompt/63_settings_enemy_anim.md delete mode 100644 prompt/64_fix_enemy_anim_sync.md delete mode 100644 prompt/65_fix_text_timing.md delete mode 100644 prompt/66_fix_double_animation.md delete mode 100644 prompt/67_revert_delay.md delete mode 100644 prompt/68_adjust_game_config.md delete mode 100644 prompt/69_sync_damage_on_impact.md delete mode 100644 prompt/70_fix_player_action_crash.md delete mode 100644 prompt/71_fix_null_error.md delete mode 100644 prompt/72_delay_enemy_intent.md delete mode 100644 prompt/73_prevent_first_turn_defense.md delete mode 100644 prompt/74_fix_item_name_interpolation.md diff --git a/lib/game/logic/loot_generator.dart b/lib/game/logic/loot_generator.dart index 19b641c..9b0ef86 100644 --- a/lib/game/logic/loot_generator.dart +++ b/lib/game/logic/loot_generator.dart @@ -16,14 +16,14 @@ class LootGenerator { int finalHp = template.hpBonus; int finalArmor = template.armorBonus; int finalLuck = template.luck; - + // 0. Normal Rarity: Prefix logic for base stat variations if (template.rarity == ItemRarity.normal) { // Weighted Random Selection final prefixes = ItemPrefixTable.normalPrefixes; int totalWeight = prefixes.fold(0, (sum, p) => sum + p.weight); int roll = _random.nextInt(totalWeight); - + ItemModifier? selectedModifier; int currentSum = 0; for (var mod in prefixes) { @@ -36,9 +36,9 @@ class LootGenerator { if (selectedModifier != null) { if (selectedModifier.prefix.isNotEmpty) { - finalName = "${selectedModifier.prefix} $template.name"; + finalName = "${selectedModifier.prefix} ${template.name}"; } - + double mult = selectedModifier.multiplier; if (mult != 1.0) { finalAtk = (finalAtk * mult).floor(); @@ -49,58 +49,77 @@ class LootGenerator { } // 1. Magic Rarity: 50% chance to get a Magic Prefix (1 stat change) else if (template.rarity == ItemRarity.magic) { - if (_random.nextDouble() < ItemConfig.magicPrefixChance) { // Use constant + if (_random.nextDouble() < ItemConfig.magicPrefixChance) { + // Use constant // Filter valid prefixes for this slot final validPrefixes = ItemPrefixTable.magicPrefixes.where((p) { - return p.allowedSlots == null || p.allowedSlots!.contains(template.slot); + return p.allowedSlots == null || + p.allowedSlots!.contains(template.slot); }).toList(); if (validPrefixes.isNotEmpty) { - final modifier = validPrefixes[_random.nextInt(validPrefixes.length)]; - finalName = "${modifier.prefix} ${template.name}"; - - modifier.statChanges.forEach((stat, value) { - switch(stat) { - case StatType.atk: finalAtk += value; break; - case StatType.maxHp: finalHp += value; break; - case StatType.defense: finalArmor += value; break; - case StatType.luck: finalLuck += value; break; - } - }); + final modifier = validPrefixes[_random.nextInt(validPrefixes.length)]; + finalName = "${modifier.prefix} ${template.name}"; + + modifier.statChanges.forEach((stat, value) { + switch (stat) { + case StatType.atk: + finalAtk += value; + break; + case StatType.maxHp: + finalHp += value; + break; + case StatType.defense: + finalArmor += value; + break; + case StatType.luck: + finalLuck += value; + break; + } + }); } } - } + } // 2. Rare Rarity: Always get a Rare Prefix (2 stat changes or stronger) else if (template.rarity == ItemRarity.rare) { - bool nameChanged = false; - - // Always generate a completely new cool name for Rare items - finalName = NameGenerator.generateName(template.slot); - nameChanged = true; + bool nameChanged = false; - // Filter valid prefixes for this slot - final validPrefixes = ItemPrefixTable.rarePrefixes.where((p) { - return p.allowedSlots == null || p.allowedSlots!.contains(template.slot); - }).toList(); + // Always generate a completely new cool name for Rare items + finalName = NameGenerator.generateName(template.slot); + nameChanged = true; - if (validPrefixes.isNotEmpty) { - final modifier = validPrefixes[_random.nextInt(validPrefixes.length)]; - - // If name wasn't already changed by NameGenerator, apply prefix to name - if (!nameChanged) { - finalName = "${modifier.prefix} ${template.name}"; - } - // Even if name changed, we STILL apply the stats from the prefix modifier! - - modifier.statChanges.forEach((stat, value) { - switch(stat) { - case StatType.atk: finalAtk += value; break; - case StatType.maxHp: finalHp += value; break; - case StatType.defense: finalArmor += value; break; - case StatType.luck: finalLuck += value; break; - } - }); - } + // Filter valid prefixes for this slot + final validPrefixes = ItemPrefixTable.rarePrefixes.where((p) { + return p.allowedSlots == null || + p.allowedSlots!.contains(template.slot); + }).toList(); + + if (validPrefixes.isNotEmpty) { + final modifier = validPrefixes[_random.nextInt(validPrefixes.length)]; + + // If name wasn't already changed by NameGenerator, apply prefix to name + if (!nameChanged) { + finalName = "${modifier.prefix} ${template.name}"; + } + // Even if name changed, we STILL apply the stats from the prefix modifier! + + modifier.statChanges.forEach((stat, value) { + switch (stat) { + case StatType.atk: + finalAtk += value; + break; + case StatType.maxHp: + finalHp += value; + break; + case StatType.defense: + finalArmor += value; + break; + case StatType.luck: + finalLuck += value; + break; + } + }); + } } // Legendary/Unique items usually keep their original names/stats. diff --git a/lib/game/model/entity.dart b/lib/game/model/entity.dart index 3815a6d..f8b1a80 100644 --- a/lib/game/model/entity.dart +++ b/lib/game/model/entity.dart @@ -48,9 +48,7 @@ class Character { 'baseDefense': baseDefense, 'gold': gold, 'image': image, - 'equipment': equipment.map( - (key, value) => MapEntry(key.name, value.id), - ), + 'equipment': equipment.map((key, value) => MapEntry(key.name, value.id)), 'inventory': inventory.map((e) => e.id).toList(), 'statusEffects': statusEffects.map((e) => e.toJson()).toList(), 'permanentModifiers': permanentModifiers.map((e) => e.toJson()).toList(), diff --git a/lib/providers/battle_provider.dart b/lib/providers/battle_provider.dart index 1de6900..c851371 100644 --- a/lib/providers/battle_provider.dart +++ b/lib/providers/battle_provider.dart @@ -369,15 +369,13 @@ class BattleProvider with ChangeNotifier { handleImpact(event); // Process impact via handleImpact for safety } - // Now check for enemy death (if applicable from bleed, or previous impacts) - if (enemy.isDead) { - // Check enemy death after player's action - _onVictory(); - return; - } + // Now check for enemy death (if applicable from bleed, or previous impacts) - _endPlayerTurn(); - } + // Removed redundant `if (enemy.isDead)` check as it's handled in `_processAttackImpact` + + _endPlayerTurn(); + + } void _endPlayerTurn() { // Update durations at end of turn @@ -630,6 +628,7 @@ class BattleProvider with ChangeNotifier { stage++; showRewardPopup = false; + rewardOptions.clear(); // Clear options to prevent flash on next victory _prepareNextStage(); diff --git a/prompt/00_project_context_restore.md b/prompt/00_project_context_restore.md index aa8dadd..76f691d 100644 --- a/prompt/00_project_context_restore.md +++ b/prompt/00_project_context_restore.md @@ -7,161 +7,84 @@ - **프로젝트명:** Colosseum's Choice - **플랫폼:** Flutter (Android/iOS/Web/Desktop) - **장르:** 텍스트 기반의 턴제 RPG + GUI (로그라이크 요소 포함) -- **상태:** 프로토타입 단계 (전투 시각화, 데이터 주도 시스템, 반응형 UI 구현 완료) +- **상태:** 핵심 시스템 구현 완료 및 안정화 (i18n 구조 적용, 애니메이션 동기화 완료) ## 2. 현재 구현된 핵심 기능 (Feature Status) ### A. 게임 흐름 (Game Flow) -1. **메인 메뉴 (`MainMenuScreen`):** 게임 시작 버튼. -2. **캐릭터 선택 (`CharacterSelectionScreen`):** 'Warrior' 직업 구현. -3. **메인 게임 (`MainWrapper`):** 하단 탭 네비게이션 (Battle / Inventory). -4. **반응형 레이아웃 (Responsive UI):** - - `ResponsiveContainer` 위젯을 통해 최대 너비(600px) 및 높이(1000px) 제한. - - 웹/태블릿 환경에서도 모바일 앱처럼 중앙 정렬된 화면 제공. - - **Battle UI Layout:** 플레이어(좌측 하단) vs 적(우측 상단) 대각선 구도 배치 및 캐릭터 임시 아이콘 적용. - - **Widget Refactoring:** `BattleScreen`의 주요 UI 컴포넌트(`CharacterStatusCard`, `BattleLogOverlay` 등)를 `lib/widgets/battle/`로 분리하여 모듈화. +1. **메인 메뉴 (`MainMenuScreen`):** 게임 시작, 이어하기(저장된 데이터 있을 시), 설정 버튼. +2. **캐릭터 선택 (`CharacterSelectionScreen`):** 'Warrior' 직업 구현 (스탯 확인 후 시작). +3. **메인 게임 (`MainWrapper`):** 하단 탭 네비게이션 (Battle / Inventory / Settings). +4. **설정 (`SettingsScreen`):** + - 적 애니메이션 활성화/비활성화 토글 (`SettingsProvider` 연동). + - 게임 재시작, 메인 메뉴로 돌아가기 기능. +5. **반응형 레이아웃 (Responsive UI):** + - `ResponsiveContainer`를 통해 다양한 화면 크기 대응 (최대 너비/높이 제한). + - Battle UI: 플레이어(좌하단) vs 적(우상단) 대각선 구도. ### B. 전투 시스템 (`BattleProvider`) - **턴제 전투:** 플레이어 턴 -> 적 턴. - **행동 선택:** 공격(Attack) / 방어(Defend). -- **리스크 시스템 (Risk System):** Safe(100%/50%), Normal(80%/100%), Risky(40%/200%) 선택. +- **리스크 시스템 (Risk System):** + - **Safe:** 성공률 100%+, 효율 50%. + - **Normal:** 성공률 80%+, 효율 100%. + - **Risky:** 성공률 40%+, 효율 200% (성공 시 강력한 이펙트). + - **Luck 보정:** `totalLuck` 1당 성공률 +1%. - **적 인공지능 (Enemy AI & Intent):** - - **Intent UI:** 적의 다음 행동(공격/방어, 리스크)을 미리 표시. - - **선제 방어 (Pre-emptive Defense):** 적이 방어를 선택하면 턴 시작 전에 즉시 방어도가 적용됨. - - **Defense Restriction:** `DefenseForbidden` 상태 시 방어 행동 선택 불가. - - **Variance Removed:** 적의 공격/방어 수치 계산 시 랜덤 분산(Variance) 제거 (고정 수치). -- **적 장비 시스템 (Enemy Equipment):** - - 적에게 아이템 장착 가능 (`enemies.json`의 `equipment` 필드). - - 장착된 아이템의 스탯 및 특수 효과(상태이상 등)가 전투 시 적용됨. -- **시각 효과 (Visual Effects):** - - **Floating Text:** 데미지 발생 시 캐릭터 위에 데미지 수치가 떠오름 (일반: 빨강, 출혈: 보라, 취약: 주황). - - **Action Feedback:** 공격 빗나감(MISS - 적 위치/회색) 및 방어 실패(FAILED - 내 위치/빨강) 텍스트 오버레이. - - **Effect Icons:** 공격/방어 및 리스크 레벨에 따른 아이콘 애니메이션 출력. - - **Advanced Animations:** - - **Risk-Based:** Safe(Wobble), Normal(Dash), Risky(Scale Up + Heavy Dash + Shake + Explosion). - - **Icon-Only:** 공격 시 캐릭터 아이콘만 이동하며, 스탯 정보(HP/Armor)는 일시적으로 숨김 처리. - - **Impact Sync:** 타격 이펙트와 데미지 텍스트가 애니메이션 타격 시점에 정확히 동기화됨. + - **Intent UI:** 적의 다음 행동(공격/방어, 데미지/방어도) 미리 표시. + - **동기화된 애니메이션:** 적 행동 결정(`_generateEnemyIntent`)은 이전 애니메이션이 완전히 끝난 후 이루어짐. + - **선제 방어:** 적이 방어 행동을 선택하면 턴 시작 시 즉시 방어도가 적용됨. +- **애니메이션 및 타격감 (Visuals & Impact):** + - **UI 주도 Impact 처리:** 애니메이션 타격 시점(`onImpact`)에 정확히 데미지가 적용되고 텍스트가 뜸 (완벽한 동기화). + - **적 돌진:** 적도 공격 시 플레이어 위치로 돌진함 (설정에서 끄기 가능). + - **이펙트:** 타격 아이콘, 데미지 텍스트(Floating Text), 화면 흔들림(`ShakeWidget`), 폭발(`ExplosionWidget`). - **상태이상:** `Stun`, `Bleed`, `Vulnerable`, `DefenseForbidden`. -- **행운 시스템 (Luck System):** - - 아이템 옵션으로 `luck` 스탯 제공. - - `totalLuck` 수치만큼 행동(공격/방어) 성공 확률 증가 (1 Luck = +1%). - - 성공 확률은 최대 100%로 제한됨. - - **UI:** 인벤토리에서 Luck 수치 확인 가능, 전투 시 Risk 선택 창에서 보정된 확률 표시. -### C. 데이터 주도 설계 (Data-Driven Design) +### C. 데이터 및 로직 (Architecture) -- **JSON 데이터:** `assets/data/items.json` (ID 포함), `assets/data/enemies.json` (장비 포함). -- **데이터 로더:** `ItemTable` (ID 조회 지원), `EnemyTable` (장비 장착 지원). +- **Data-Driven:** `items.json`, `enemies.json`, `players.json`. +- **Logic 분리:** + - `BattleProvider`: UI 상태 관리 및 이벤트 스트림(`damageStream`, `effectStream`) 발송. + - `CombatCalculator`: 데미지 공식, 확률 계산, 상태이상 로직 순수 함수화. + - `BattleLogManager`: 전투 로그 관리. + - `LootGenerator`: 아이템 생성, 접두사(Prefix) 부여, 랜덤 스탯 로직. + - `SettingsProvider`: 전역 설정(애니메이션 on/off 등) 관리 및 영구 저장. +- **Soft i18n:** UI 텍스트는 `lib/game/config/app_strings.dart`에서 통합 관리. +- **Config:** `GameConfig`, `BattleConfig`, `ItemConfig` 등 설정 값 중앙화. -### D. 아이템 및 경제 (`Item`, `Inventory`) +### D. 아이템 및 경제 - **장비:** 무기, 방어구, 방패, 장신구. -- **아이콘 및 색상 (`ItemUtils`):** - - 무기: 빨강 삼각형 (`Icons.change_history`) - - 방패: 파랑 방패 (`Icons.shield`) - - 갑옷: 파랑 옷 (`Icons.checkroom`) - - 장신구: 보라 다이아몬드 (`Icons.diamond`) -- **가격:** JSON 고정 가격 사용. 판매 시 60% (`GameMath.floor`) 획득. -- **인벤토리:** 장착 슬롯 및 가방(Bag) 그리드 UI 구현. -- **아이템 시스템 (Item System):** - - **Rarity (희귀도):** Common, Rare, Epic, Legendary. (드랍 확률 관여) - - **Tier (티어):** 1티어(초반), 2티어(중반), 3티어(후반). (라운드 진행도에 따라 등장 제한) - - **획득 로직:** 현재 라운드(Tier)에 맞는 아이템 풀 내에서 Rarity 확률에 따라 결정. +- **시스템:** + - **Rarity:** Common ~ Unique. + - **Tier:** 라운드 진행도에 따라 상위 티어 아이템 등장. + - **Prefix:** Rarity에 따라 접두사가 붙으며 스탯이 변형됨 (예: "Sharp Wooden Sword"). +- **상점 (`ShopProvider`):** 아이템 구매/판매, 리롤(Reroll), 인벤토리 관리. -### E. 스테이지 시스템 (`StageModel`) +### E. 저장 및 진행 (Persistence) -- **타입:** Battle, Shop, Rest, Elite. -- **적 등장 테이블 (Enemy Pool):** 적 조우 시, 현재 스테이지(라운드)에 따라 등장 가능한 적 목록(`enemy_table_pool`)을 설정하여 해당 범위 내에서 적을 랜덤 생성해야 함. -- **게임 구조 (Game Structure):** - - **총 3라운드 (3 Rounds):** 각 라운드는 12개의 스테이지로 구성 (12/12/12). - - **라운드 구성:** - 1. **1라운드:** 지하 불법 투기장 (Underground Illegal Arena) - 2. **2라운드:** 콜로세움 (Colosseum) - 3. **3라운드:** 왕의 투기장 (King's Arena) - 최종 보스(Final Boss) 등장. +- **자동 저장:** 스테이지 클리어 시 `SaveManager`를 통해 자동 저장. +- **Permadeath:** 패배 시 저장 데이터 삭제 (로그라이크 요소). -### F. 시스템 및 설정 (System & Settings) +## 3. 작업 컨벤션 (Working Conventions) -- **설정 페이지 (Settings Screen):** - - 게임 재시작 (Restart Game) 및 메인 메뉴로 돌아가기 (Return to Main Menu) 기능. - - 하단 네비게이션 바(BottomNavigationBar)에 설정 탭 추가. -- **로컬 저장 (Local Storage):** - - `shared_preferences`를 사용하여 스테이지 클리어 시 자동 저장. - - 메인 메뉴에서 '이어하기 (CONTINUE)' 버튼을 통해 저장된 시점부터 게임 재개 가능. - - 저장 데이터: 스테이지 진행도, 턴 수, 플레이어 상태(체력, 장비, 인벤토리 등). +- **Prompt Driven Development:** `prompt/XX_description.md` 유지. (유사 작업 통합 및 인덱스 정리 권장) +- **i18n Strategy (Soft i18n):** UI에 표시되는 문자열은 하드코딩하지 않고 `lib/game/config/app_strings.dart`의 상수를 사용해야 합니다. (전투 로그 등 동적 문자열 제외) +- **Config Management:** 하드코딩되는 값들은 `config` 폴더 내 파일들(`lib/game/config/` 등)에서 통합 관리할 수 있도록 작성해야 합니다. +- **State Management:** `Provider` (UI 상태) + `Stream` (이벤트성 데이터). +- **Data:** JSON 기반 + `Table` 클래스로 로드. -## 3. 핵심 파일 및 아키텍처 +## 4. 최근 주요 변경 사항 (Change Log) -- **`lib/providers/battle_provider.dart`:** - - **Core Logic:** 상태 관리, 전투 루프. - - **Streams:** `damageStream`, `effectStream`을 통해 UI(`BattleScreen`)에 비동기 이벤트 전달. -- **`lib/game/enums.dart`:** 프로젝트 전반의 Enum 통합 관리 (`ActionType`, `RiskLevel`, `StageType` 등). -- **`lib/utils/item_utils.dart`:** 아이템 타입별 아이콘 및 색상 로직 중앙화. -- **`lib/widgets/battle/`:** `BattleScreen`에서 분리된 재사용 가능한 위젯들. - - **UI Components:** `CharacterStatusCard`, `BattleLogOverlay`, `FloatingBattleTexts`, `StageUI`. - - **Effects:** `BattleAnimationWidget` (공격 애니메이션), `ExplosionWidget` (파티클), `ShakeWidget` (화면 흔들림). -- **`lib/widgets/responsive_container.dart`:** 반응형 레이아웃 컨테이너. -- **`lib/game/model/`:** - - `damage_event.dart`, `effect_event.dart`: 이벤트 모델. - - `entity.dart`: `Character` (Player/Enemy). - - `item.dart`: `Item` (ID 필드 포함). -- **`lib/screens/battle_screen.dart`:** - - `StreamSubscription`을 통해 이펙트 이벤트 수신 및 `Overlay` 애니메이션 렌더링. - - `Stack` 및 `Positioned` 기반의 정교한 레이아웃. +- **[Refactor] BattleProvider:** `CombatCalculator`, `BattleLogManager`, `LootGenerator` 분리로 코드 다이어트. +- **[Refactor] Animation Sync:** `Future.delayed` 예측 방식을 버리고, UI 애니메이션 콜백(`onImpact`)을 통해 로직을 트리거하는 방식으로 변경하여 타격감 동기화 해결. +- **[Refactor] Settings:** `SettingsProvider` 도입 및 적 애니메이션 토글 기능 추가. +- **[Fix] Bugs:** 아이템 이름 생성 오류 수정, 리워드 팝업 깜빡임 및 중복 생성 수정, 앱 크래시(Null Safety) 수정. -## 4. 작업 컨벤션 (Working Conventions) +## 5. 다음 단계 (Next Steps) -- **Prompt Driven Development:** `prompt/XX_description.md` 유지. - - **유사 작업 통합:** 작업 내용이 이전 프롬프트와 유사한 경우 새로운 프롬프트를 생성하지 않고 기존 프롬프트에 내용을 추가합니다. -- **Language:** **모든 프롬프트 파일(prompt/XX\_...)은 반드시 한국어(Korean)로 작성해야 합니다.** - - **Config Management:** 하드코딩되는 값들은 `config` 폴더 내 파일들(`lib/game/config/` 등)에서 통합 관리할 수 있도록 작성해야 합니다. - - **i18n Strategy (Soft i18n):** UI에 표시되는 문자열은 하드코딩하지 않고 `lib/game/config/app_strings.dart`의 상수를 사용해야 합니다. (전투 로그 등 동적 문자열 제외) - - **State Management:** `Provider` + `Stream` (이벤트성 데이터).- **Data:** JSON 기반. - -## 5. 다음 단계 작업 (Next Steps) - -1. **아이템 시스템 고도화:** `items.json`에 `rarity`, `tier` 필드 추가 및 `ItemTable` 로직 수정. -2. **[x] 상점 구매 기능:** `Shop` 스테이지 구매 UI 구현 (Tier/Rarity 기반 목록 생성). -3. **적 등장 테이블 구현:** 스테이지별 등장 가능한 적 목록(`enemy_table_pool`) 설정 및 적용. -4. **이미지 리소스 적용:** JSON 경로에 맞는 실제 이미지 파일 추가 및 UI 표시. -5. **밸런싱 및 콘텐츠 확장:** 아이템/적 데이터 추가 및 밸런스 조정. - -## 6. 장기 목표 (Future Roadmap / TODO) - -- [ ] **출혈 상태 이상 조건 변경:** 공격 시 상대방의 방어도에 의해 공격이 완전히 막힐 경우, 출혈 상태 이상이 적용되지 않도록 로직 변경. -- [ ] **장비 분해 시스템 (적 장비):** 플레이어 장비의 옵션으로 상대방의 장비를 분해하여 '언암드' 상태로 만들 수 있는 시스템 구현. -- [ ] **플레이어 공격 데미지 분산(Variance) 적용 여부 검토:** 현재 적에게만 적용된 +/- 20% 데미지 분산을 플레이어에게도 적용할지 결정. -- [x] **애니메이션 및 타격감 고도화:** - - 캐릭터별 이미지 추가 및 하스스톤 스타일의 공격 모션(대상에게 돌진 후 타격) 구현 완료 (Icon-Only Animation). - - **Risky 공격:** 하스스톤의 7데미지 이상 타격감(화면 흔들림, 강렬한 파티클 및 임팩트) 구현 완료. -- [ ] **체력 조건부 특수 능력:** 캐릭터의 체력이 30% 미만일 때 발동 가능한 특수 능력 시스템 구현. -- [ ] **영구 스탯 수정자 로직 적용 (필수):** - - 현재 `Character` 클래스에 `permanentModifiers` 필드만 선언되어 있음. - - 추후 `totalAtk`, `totalDefense`, `totalMaxHp` 계산 시 이 수정자들을 반드시 반영해야 함. -- [ ] **Google OAuth 로그인 및 계정 연동:** - - Firebase Auth 등을 활용한 구글 로그인 구현. - - Firestore 또는 Realtime Database에 유저 계정 정보(진행 상황, 재화 등) 저장 및 불러오기 기능 추가. - - _Note: 이 기능은 게임의 핵심 로직이 안정화된 후, 완전 나중에 진행할 예정입니다._ -- [ ] **설정 페이지 (Settings Page) 구현 (Priority: Very Low):** - - **이펙트 강도 조절 (Effect Intensity):** 1 ~ 999 범위로 설정 가능. - - **Easter Egg:** 강도를 999로 설정하고 Risky 공격 성공 시, "심각한 오류로 프로세스가 종료되었습니다" 같은 페이크 시스템 팝업 출력. - ---- - -**이 프롬프트를 읽은 AI 에이전트는 위 내용을 바탕으로 즉시 개발을 이어가십시오.** - -## 7. 프롬프트 히스토리 (Prompt History) - -- [x] 45_config_refactoring.md -- [x] 46_shop_refactoring.md -- [x] 47_inventory_full_handling.md -- [x] 48_refactor_stage_ui.md -- [x] 49_implement_item_icons.md -- [x] 50_expand_item_pool.md -- [x] 51_refactor_prefix_table.md -- [x] 52_round_based_enemy_pool.md -- [x] 53_refine_stage_rewards.md -- [x] 54_fix_shop_logic.md -- [x] 55_fix_shop_ui_sync.md -- [x] 56_permadeath_implementation.md +1. **밸런싱:** 현재 몬스터 및 아이템 스탯 미세 조정. +2. **콘텐츠 확장:** 더 많은 아이템, 적, 스킬 패턴 추가. +3. **튜토리얼:** 신규 유저를 위한 가이드 추가. \ No newline at end of file diff --git a/prompt/60_introduce_app_strings.md b/prompt/60_introduce_app_strings.md deleted file mode 100644 index 3d9cbef..0000000 --- a/prompt/60_introduce_app_strings.md +++ /dev/null @@ -1,16 +0,0 @@ -# 60. Introduce AppStrings for Soft i18n - -## 1. 목표 (Goal) -- UI 텍스트 하드코딩을 방지하고 추후 본격적인 i18n 적용을 대비하기 위해 `AppStrings` 상수 클래스를 도입합니다. -- 자주 사용되는 UI 텍스트(메뉴, 스탯, 행동 등)를 한 곳에서 관리합니다. - -## 2. 구현 계획 (Implementation Plan) -1. **`lib/game/config/app_strings.dart` 생성:** - - `actionAttack`, `actionDefend`, `statHp`, `statAtk` 등 카테고리별로 정적 상수를 정의합니다. -2. **UI 코드 수정:** - - `BattleScreen`, `InventoryScreen`, `MainMenuScreen` 등에서 하드코딩된 문자열을 `AppStrings.xxx`로 교체합니다. - - *Note:* 전투 로그와 같이 동적으로 생성되는 복잡한 문장은 이번 단계에서 제외합니다. - -## 3. 기대 효과 (Expected Outcome) -- 텍스트 변경 시 `AppStrings`만 수정하면 되므로 유지보수성 향상. -- 추후 다국어 지원 라이브러리 도입 시 마이그레이션이 매우 쉬워짐. diff --git a/prompt/61_system_stabilization_summary.md b/prompt/61_system_stabilization_summary.md new file mode 100644 index 0000000..e3362db --- /dev/null +++ b/prompt/61_system_stabilization_summary.md @@ -0,0 +1,27 @@ +# 61. System Stabilization & Refactoring (Summary) + +## 1. 개요 +이 문서는 프로젝트 안정화 및 리팩토링 과정에서 진행된 61번부터 76번까지의 작업 내용을 요약 및 통합한 것입니다. + +## 2. 주요 변경 사항 + +### A. 구조 개선 및 리팩토링 +- **i18n 적용 (Soft i18n):** `AppStrings.dart`를 도입하여 UI 텍스트를 중앙화했습니다. +- **설정 시스템 (`SettingsProvider`):** 적 애니메이션 On/Off 등 게임 설정을 관리하고 영구 저장하는 시스템을 구축했습니다. +- **전투 로직 동기화 (UI-Driven Impact):** + - 기존 `Future.delayed` 기반의 불안정한 타이밍 로직을 제거했습니다. + - UI(`BattleScreen`)의 애니메이션 타격 시점(`onImpact`)에 `BattleProvider`의 데미지 로직(`handleImpact`)을 호출하는 구조로 변경하여 시각 효과와 데이터 처리를 완벽하게 동기화했습니다. +- **적 Intent 생성 지연:** 적의 공격 애니메이션이 완전히 끝난 후 다음 행동을 결정하도록 하여, 시각적 혼란(공격 중 방어 이펙트 출력 등)을 방지했습니다. + +### B. 버그 수정 +- **Null Safety Crash:** 공격 실패 시 `EffectEvent`의 null 값을 참조하여 앱이 종료되는 문제를 수정했습니다. +- **리워드 시스템:** + - 리워드 팝업이 깜빡이거나 이전 데이터를 보여주는 문제 수정. + - 승리 시 리워드가 중복 생성(두 번 호출)되는 문제 수정. +- **아이템 이름:** `LootGenerator`의 문자열 보간 오류로 인해 "Instance of..."가 출력되던 문제를 수정했습니다. +- **애니메이션 중복:** 적 캐릭터 카드에 애니메이션 위젯이 중복 적용되어 발생하던 이상 현상을 수정했습니다. + +### C. 기능 추가 +- **적 공격 애니메이션:** 플레이어와 마찬가지로 적도 공격 시 대상을 향해 돌진하는 애니메이션을 추가했습니다. + +이 작업들을 통해 게임의 안정성, 코드의 유지보수성, 그리고 플레이어의 시각적 경험이 크게 향상되었습니다. diff --git a/prompt/61_update_convention_i18n.md b/prompt/61_update_convention_i18n.md deleted file mode 100644 index 5910de1..0000000 --- a/prompt/61_update_convention_i18n.md +++ /dev/null @@ -1,11 +0,0 @@ -# 61. Convention Update (Soft i18n) - -## 1. 목표 (Goal) -- 프로젝트 컨텍스트 복구용 파일(`00_project_context_restore.md`)에 최근 도입한 `AppStrings` 사용 규칙(Soft i18n)을 명시하여, 향후 개발 시 일관성을 유지합니다. - -## 2. 변경 내용 (Changes) -- `4. 작업 컨벤션 (Working Conventions)` 섹션에 `i18n Strategy` 항목 추가. -- "UI에 표시되는 문자열은 하드코딩하지 않고 `lib/game/config/app_strings.dart`의 상수를 사용해야 합니다." 라는 지침 명시. - -## 3. 기대 효과 (Expected Outcome) -- 이후 AI 세션이나 다른 개발자가 프로젝트에 참여할 때, UI 텍스트 작성 규칙을 즉시 인지하고 따를 수 있음. diff --git a/prompt/62_enemy_attack_animation.md b/prompt/62_enemy_attack_animation.md deleted file mode 100644 index 76f47ad..0000000 --- a/prompt/62_enemy_attack_animation.md +++ /dev/null @@ -1,17 +0,0 @@ -# 62. Implement Enemy Attack Animation - -## 1. 목표 (Goal) -- 플레이어뿐만 아니라 적(Enemy)이 공격할 때도 플레이어 쪽으로 돌진하는 애니메이션을 추가하여 전투의 역동성을 높입니다. - -## 2. 구현 계획 (Implementation Plan) -1. **`BattleScreen` 수정:** - - `GlobalKey _enemyAnimKey`를 추가합니다. - - 적 캐릭터 UI(`CharacterStatusCard`)를 `BattleAnimationWidget`으로 감쌉니다. -2. **애니메이션 트리거 로직 (`_addFloatingEffect`):** - - 기존 플레이어 공격 감지 로직과 유사하게, `event.type == ActionType.attack`이고 `event.target == EffectTarget.player`인 경우를 감지합니다. - - 적 위치에서 플레이어 위치로의 오프셋(`playerPos - enemyPos`)을 계산하여 `_enemyAnimKey`로 애니메이션을 실행합니다. - - 공격 모션 중에는 적의 스탯 정보(HP바 등)를 일시적으로 숨기는 로직(`_isEnemyAttacking`)도 추가합니다. - -## 3. 기대 효과 (Expected Outcome) -- 적의 턴에도 시각적인 움직임이 발생하여 전투가 더 생동감 있게 느껴짐. -- 플레이어와 적의 상호작용이 명확해짐. diff --git a/prompt/63_settings_enemy_anim.md b/prompt/63_settings_enemy_anim.md deleted file mode 100644 index f8a76a9..0000000 --- a/prompt/63_settings_enemy_anim.md +++ /dev/null @@ -1,22 +0,0 @@ -# 63. Implement Settings Provider and Enemy Animation Toggle - -## 1. 목표 (Goal) -- 적 공격 애니메이션을 기본적으로 비활성화하고, 설정 화면에서 유저가 선택적으로 활성화할 수 있도록 합니다. -- 전역 설정을 관리하는 `SettingsProvider`를 도입합니다. - -## 2. 구현 계획 (Implementation Plan) -1. **`SettingsProvider` 구현:** - - `lib/providers/settings_provider.dart` 생성. - - `enableEnemyAnimations` boolean 상태 관리 (기본값 false). - - `SharedPreferences`를 이용한 영구 저장(`settings_enemy_anim`). -2. **`main.dart` 등록:** - - `MultiProvider`에 `SettingsProvider` 추가. -3. **`SettingsScreen` UI:** - - `AppStrings`에 관련 텍스트 추가. - - SwitchListTile 위젯을 사용하여 설정 변경 UI 구현. -4. **`BattleScreen` 로직:** - - `_addFloatingEffect` 메서드 내에서 적 애니메이션 실행 전 `context.read().enableEnemyAnimations` 확인. - -## 3. 기대 효과 (Expected Outcome) -- 유저가 게임의 연출 빈도를 제어할 수 있음. -- 설정 관리의 기반이 마련됨. diff --git a/prompt/64_fix_enemy_anim_sync.md b/prompt/64_fix_enemy_anim_sync.md deleted file mode 100644 index 79c269f..0000000 --- a/prompt/64_fix_enemy_anim_sync.md +++ /dev/null @@ -1,14 +0,0 @@ -# 64. Fix Enemy Animation Sync - -## 1. 목표 (Goal) -- 적 공격 시, 애니메이션이 끝나기 전에 데미지가 먼저 들어가는 동기화 문제를 해결합니다. - -## 2. 원인 (Cause) -- `BattleProvider`의 `_enemyTurn` 메서드에서 `EffectEvent`를 보낸 후, 애니메이션 재생 시간만큼 기다리지 않고 즉시 데미지 로직을 수행하고 있습니다. - -## 3. 해결 방안 (Solution) -- `_enemyTurn` 메서드 내에서 공격 성공 시, `EffectEvent`를 전송한 직후에 `Future.delayed`를 추가하여 애니메이션이 재생될 시간을 확보합니다. -- 대기 시간은 `GameConfig`에 정의된 `animDelaySafe`, `animDelayNormal`, `animDelayRisky` 상수를 사용합니다. - -## 4. 기대 효과 (Expected Outcome) -- 적이 플레이어에게 돌진하여 타격하는 시점에 정확히 데미지 텍스트가 뜨고 HP가 감소합니다. diff --git a/prompt/65_fix_text_timing.md b/prompt/65_fix_text_timing.md deleted file mode 100644 index 478b353..0000000 --- a/prompt/65_fix_text_timing.md +++ /dev/null @@ -1,13 +0,0 @@ -# 65. Fix Floating Text Timing - -## 1. 목표 (Goal) -- 적이 플레이어에게 도착하기도 전에 데미지 텍스트가 먼저 뜨는 문제를 해결합니다. - -## 2. 원인 (Cause) -- `BattleProvider`에서 주는 `Future.delayed` 시간이 실제 UI 상의 '적 돌진 애니메이션 소요 시간'보다 짧거나 비슷하여, 미세한 차이로 `DamageEvent`가 먼저 처리됨. - -## 3. 해결 방안 (Solution) -- `BattleProvider`의 `_enemyTurn` 메서드에서 사용하는 딜레이 시간에 **여유분(+300ms)**을 추가하여, 확실하게 애니메이션이 타격 지점에 도달한 후에 데미지 이벤트가 발생하도록 조정합니다. - -## 4. 기대 효과 (Expected Outcome) -- 적이 돌진 -> 타격(Impact) -> 그 순간에 데미지 텍스트 출력 순서가 자연스럽게 맞음. diff --git a/prompt/66_fix_double_animation.md b/prompt/66_fix_double_animation.md deleted file mode 100644 index 1738431..0000000 --- a/prompt/66_fix_double_animation.md +++ /dev/null @@ -1,15 +0,0 @@ -# 66. Fix Double Animation Wrapper - -## 1. 목표 (Goal) -- 적 공격 애니메이션 시 타격 이펙트가 이상하게 동작하거나 중복되는 문제를 해결합니다. - -## 2. 원인 (Cause) -- `CharacterStatusCard`는 내부적으로 이미 `BattleAnimationWidget`을 포함하고 있습니다. -- 그러나 `BattleScreen`에서 적 카드를 또다시 `BattleAnimationWidget`으로 감싸는 실수를 범하여 위젯이 중복되었습니다. - -## 3. 해결 방안 (Solution) -- `BattleScreen`에서 적 카드를 감싸고 있는 `BattleAnimationWidget`을 제거합니다. -- 대신 `CharacterStatusCard`의 `animationKey` 파라미터에 `_enemyAnimKey`를 직접 전달합니다 (플레이어와 동일한 방식). - -## 4. 기대 효과 (Expected Outcome) -- 위젯 구조가 단순화되고 애니메이션 제어가 정상적으로 동작합니다. diff --git a/prompt/67_revert_delay.md b/prompt/67_revert_delay.md deleted file mode 100644 index 3fd92fa..0000000 --- a/prompt/67_revert_delay.md +++ /dev/null @@ -1,12 +0,0 @@ -# 67. Revert Enemy Turn Delay Buffer - -## 1. 목표 (Goal) -- `BattleProvider`의 `_enemyTurn` 로직을 `playerAction`과 완전히 동일하게 만듭니다. (임시로 추가했던 `+300ms` 버퍼 제거) - -## 2. 이유 (Reason) -- 이전 단계에서 적 애니메이션 위젯이 중복 생성되어 타이밍 이슈가 발생했던 것으로 파악되었습니다. -- 위젯 중복 문제가 해결되었으므로, 이제 인위적인 딜레이 없이도 정상 동작할 것으로 예상됩니다. -- 사용자의 요구("데미지 계산 자체가 플레이어와 동일해야 한다")를 충족시키기 위해 코드를 통일합니다. - -## 3. 작업 내용 (Action) -- `BattleProvider.dart` 내 `_enemyTurn` 메서드의 `Future.delayed`에서 `+ 300` 제거. diff --git a/prompt/68_adjust_game_config.md b/prompt/68_adjust_game_config.md deleted file mode 100644 index 9dc1ad6..0000000 --- a/prompt/68_adjust_game_config.md +++ /dev/null @@ -1,20 +0,0 @@ -# 68. Global Animation Delay Adjustment - -## 1. 목표 (Goal) -- 플레이어와 적 모두에게 적용되는 애니메이션 딜레이 설정(`GameConfig`)을 조정하여, 데미지 텍스트가 애니메이션 타격(Impact)보다 먼저 뜨는 현상을 근본적으로 해결합니다. - -## 2. 원인 (Cause) -- `GameConfig`의 대기 시간이 `BattleAnimationWidget`의 재생 시간과 **완벽하게 동일(ms 단위)**하게 설정되어 있습니다. -- 코드 실행 컨텍스트 차이로 인해 `Timer`(로직 대기)가 `Animation`(UI 렌더링)보다 미세하게 먼저 완료될 수 있어, 텍스트가 먼저 뜨는 "경쟁 상태(Race Condition)"가 발생합니다. - -## 3. 해결 방안 (Solution) -- `GameConfig`의 `animDelay...` 값들을 기존 값에서 **+100ms** 증가시킵니다. -- 이는 플레이어와 적 모두에게 동일하게 적용되므로 로직의 일관성을 해치지 않으면서, 시각적으로 "타격 후 텍스트"라는 자연스러운 순서를 보장합니다. - -## 4. 변경 값 -- Safe: 500 -> 600 -- Normal: 400 -> 500 -- Risky: 1100 -> 1200 - -## 5. 기대 효과 (Expected Outcome) -- 플레이어와 적 모두 공격 시 애니메이션이 목표에 도달한 직후 데미지 텍스트가 표시됨. diff --git a/prompt/69_sync_damage_on_impact.md b/prompt/69_sync_damage_on_impact.md deleted file mode 100644 index 4d913f9..0000000 --- a/prompt/69_sync_damage_on_impact.md +++ /dev/null @@ -1,29 +0,0 @@ -# 69. Sync Damage to Animation Impact (UI-Driven) - -## 1. 목표 (Goal) -- 플레이어와 적 모두의 공격 시 데미지 텍스트 출력이 애니메이션 타격(Impact) 순간과 완벽하게 동기화되도록 수정합니다. - -## 2. 문제점 (Problem) -- 기존에는 `BattleProvider`가 `Future.delayed`로 애니메이션 시간을 예측했으나, 이는 UI의 실제 Impact 시점과 미묘하게 어긋나 데미지 텍스트가 먼저 뜨는 현상이 발생했습니다. - -## 3. 해결 방안 (Solution) -- **UI (BattleScreen)가 애니메이션 Impact 시점을 `BattleProvider`에게 직접 알려주어 데미지 처리를 트리거**하는 "UI 주도 Impact 처리" 방식으로 전환합니다. - -## 4. 구현 계획 (Implementation Plan) - -### A. `EffectEvent` 데이터 확장 (`lib/game/model/effect_event.dart`) -- `EffectEvent`에 `attacker`, `target` (Character 객체), `damage`, `risk`, `isSuccess` 등 Impact 시점에 필요한 모든 정보를 담습니다. - -### B. `BattleProvider` 수정 (`lib/providers/battle_provider.dart`) -1. **`Future.delayed` 제거:** `playerAction` 및 `_enemyTurn`의 공격 로직에서 `await Future.delayed(...)`를 제거합니다. -2. **데미지 처리 로직 추출:** 공격 Impact 시점에 발생해야 할 모든 로직(HP 감소, `DamageEvent` 전송, 로그 기록, 상태이상 적용)을 `_processAttackImpact`라는 `private` 또는 `public` 메서드로 추출합니다. -3. **새로운 `public` 메서드 추가:** `BattleProvider.handleAttackImpact(String eventId, Character attacker, Character target, int damage, RiskLevel risk, bool isSuccess)`와 같은 메서드를 만들어, `BattleScreen`에서 Impact 시점에 호출하도록 합니다. - -### C. `BattleScreen` 수정 (`lib/screens/battle_screen.dart`) -1. `_addFloatingEffect` 메서드 내에서 `animateAttack`를 호출할 때: - - `onImpact` 콜백 내에서 `context.read().handleAttackImpact(...)`를 호출합니다. - - `EffectEvent`에 담긴 정보를 기반으로 `handleAttackImpact`에 인자를 전달합니다. - -## 5. 기대 효과 (Expected Outcome) -- 애니메이션과 데미지 텍스트 출력이 완벽하게 동기화되어, 게임의 타격감이 대폭 향상됩니다. -- 로직과 UI 간의 역할 분리가 명확해집니다. diff --git a/prompt/70_fix_player_action_crash.md b/prompt/70_fix_player_action_crash.md deleted file mode 100644 index 072c942..0000000 --- a/prompt/70_fix_player_action_crash.md +++ /dev/null @@ -1,16 +0,0 @@ -# 70. Fix Player Action Crash and Double Damage - -## 1. 문제 (Problem) -- **Crash:** `BattleProvider.playerAction`에서 생성하는 `EffectEvent`에 `attacker`, `targetEntity`, `damageValue` 등이 누락되어 있어, UI가 `handleImpact`를 호출할 때 `event.attacker!` 등에서 Null Pointer Exception이 발생함. -- **Double Damage:** `playerAction` 메서드 내부에 구버전의 데미지 적용 로직이 남아 있어, 이를 수정하지 않으면 데미지가 두 번 들어감. - -## 2. 해결 방안 (Solution) -- `playerAction` 메서드를 대대적으로 수정하여 `_enemyTurn`과 동일한 패턴을 적용합니다. -- **즉시 데미지 적용 로직 삭제:** HP/Armor 감소 로직을 제거합니다. -- **`EffectEvent` 완전체 생성:** `attacker`, `targetEntity`, `damageValue` 등을 모두 채워서 이벤트를 생성합니다. -- **위임:** 모든 데미지 처리는 `EffectEvent`를 통해 `handleImpact` -> `_processAttackImpact`로 위임됩니다. - -## 3. 기대 효과 (Expected Outcome) -- 앱 크래시 해결. -- 플레이어 공격 시 애니메이션과 데미지 적용 시점이 정확히 일치. -- 데미지 중복 적용 방지. diff --git a/prompt/71_fix_null_error.md b/prompt/71_fix_null_error.md deleted file mode 100644 index d82feeb..0000000 --- a/prompt/71_fix_null_error.md +++ /dev/null @@ -1,12 +0,0 @@ -# 71. Fix Null Pointer Exception on Failed Actions - -## 1. 문제 (Problem) -- `BattleProvider`에서 공격 실패(`Miss`) 또는 방어(`Defend`) 시 `_processAttackImpact`를 직접 호출하고 있음. -- `_processAttackImpact`는 `damageValue!`와 같이 강제 언래핑을 수행하므로, 실패한 공격(`damageValue`가 null)일 경우 앱이 크래시됨(`Unexpected null value`). - -## 2. 해결 방안 (Solution) -- `playerAction` 및 `_enemyTurn` 메서드에서 `_processAttackImpact` 직접 호출을 모두 `handleImpact(event)` 호출로 변경합니다. -- `handleImpact` 메서드 내부에 있는 `if (!event.isSuccess!) return;` 안전 장치를 활용하여 크래시를 방지합니다. - -## 3. 기대 효과 (Expected Outcome) -- 공격 실패 시에도 앱이 정상적으로 동작하며, 로그만 출력되고 데미지 로직은 스킵됨. diff --git a/prompt/72_delay_enemy_intent.md b/prompt/72_delay_enemy_intent.md deleted file mode 100644 index 7c8fa3c..0000000 --- a/prompt/72_delay_enemy_intent.md +++ /dev/null @@ -1,11 +0,0 @@ -# 72. Delay Enemy Intent Generation - -## 1. 문제 (Problem) -- 적이 공격하는 도중에 `_generateEnemyIntent()`가 호출되어, 다음 턴 행동(예: 방어)이 미리 실행되고 이펙트가 겹쳐 보이는 현상 발생. - -## 2. 해결 방안 (Solution) -- `BattleProvider._enemyTurn` 메서드에서 `_generateEnemyIntent()` 호출 전에 **충분한 대기 시간(`GameConfig.animDelayEnemyTurn` 또는 공격 애니메이션 시간)**을 둡니다. -- 이를 통해 적의 현재 행동 연출이 완전히 끝난 뒤에 다음 행동을 준비하도록 순서를 정리합니다. - -## 3. 기대 효과 (Expected Outcome) -- 적 공격 -> 복귀 -> (잠시 후) -> 다음 턴 방어 준비(이펙트) 순서로 자연스럽게 진행됨. diff --git a/prompt/73_prevent_first_turn_defense.md b/prompt/73_prevent_first_turn_defense.md deleted file mode 100644 index 24572fc..0000000 --- a/prompt/73_prevent_first_turn_defense.md +++ /dev/null @@ -1,81 +0,0 @@ -# 73. Prevent Pre-emptive Defense on First Turn - -## 1. 문제 (Problem) -- 현재 시스템은 `_generateEnemyIntent`가 호출되면, 적의 행동이 `Defend`일 경우 **즉시 방어도를 증가**시킵니다(Pre-emptive Defense). -- 스테이지 시작(`_prepareNextStage`) 시에도 `_generateEnemyIntent`를 호출하므로, 첫 턴부터 적이 방어도를 가지고 시작하는 경우가 발생합니다. -- 사용자는 첫 턴에는 적의 방어도가 적용되지 않은 상태로 플레이어가 선공할 수 있기를 원합니다. - -## 2. 해결 방안 (Solution) -- `_generateEnemyIntent` 메서드에 `applyImmediateEffect` (기본값 `true`) 파라미터를 추가합니다. -- `_prepareNextStage`에서 첫 Intent를 생성할 때는 `applyImmediateEffect: false`로 호출하여, 의도(Intent)는 생성하되 **방어도 증가 효과는 적용하지 않도록** 합니다. -- 단, 이렇게 하면 적이 "방어하겠다"고 표시만 하고 실제로는 방어도가 0인 상태가 됩니다. - - 만약 "첫 턴에는 적이 아예 행동하지 않음"을 원한다면 Intent 자체를 생성하지 않아야 하지만, 로그라이크 특성상 "적의 다음 행동"은 보여줘야 합니다. - - 따라서 **"첫 턴의 방어 Intent는 방어도를 올리지 않는다"** 보다는 **"첫 턴 Intent 생성 시에는 방어 행동을 제외하거나, 방어도가 턴 시작 시(적 턴)에 올라가도록"** 해야 할 수도 있습니다. - - 하지만 사용자의 요청은 "첫 턴에 방어도가 적용되는 중"인 것을 막는 것이므로, **첫 Intent 생성 시에는 방어도 적용을 스킵**하는 것이 가장 직관적입니다. - -## 3. 수정 계획 -1. `_generateEnemyIntent({bool applyDefense = true})` 로 시그니처 변경. -2. `_prepareNextStage`에서는 `applyDefense: false`로 호출. -3. 나머지 `_enemyTurn` 등에서는 `applyDefense: true`로 호출. - -**주의:** 이렇게 하면 적의 첫 행동이 `Defend`일 때, UI에는 "Defends for 10"이라고 뜨지만 실제 방어도는 0입니다. 플레이어가 공격하면 0인 상태로 맞습니다. 그리고 적 턴이 되면? 적은 `Defend` 행동을 했으므로 (이미 했다고 치고) 아무것도 안 하거나, 그때 방어도를 올릴까요? -현재 로직: `_enemyTurn`에서 `Defend`면 "Enemy maintains defensive stance" 로그만 띄우고 끝납니다. -즉, **첫 턴에 방어도를 안 올리면, 적은 첫 턴에 아무것도 안 하는 바보가 됩니다.** - -**재해석:** -"첫 턴에는 무조건 플레이어가 행동하는 걸로 해야 해" -> **"적은 첫 턴에 방어 행동(선제 방어)을 하지 말아야 한다."** -즉, **첫 턴 Intent 생성 시에는 `Defend`가 나오지 않도록 강제**하거나, **첫 턴에는 Intent를 생성하되 방어 효과는 적 턴 시작 시점에 발동하도록 유예**해야 합니다. - -하지만 `Slay the Spire` 같은 게임을 보면, 첫 턴에 적이 방어 자세를 취할 수도 있습니다. 사용자가 원하는 건 **"전투 시작하자마자 적이 단단해져 있는 게 싫다"**는 것입니다. - -**결론:** -`_prepareNextStage`에서 Intent를 생성할 때, **방어 행동이 당첨되더라도 방어도를 즉시 적용하지 않고, 적 턴이 시작될 때 적용**하도록 로직을 분산시켜야 합니다. -하지만 현재 구조상 `Intent`는 "이미 적용됨"을 가정합니다. - -**가장 깔끔한 방법:** -**첫 턴(`_prepareNextStage`)에서는 `_generateEnemyIntent`를 호출하지 않습니다.** -대신 `null`로 두거나, **"대기(Wait/Stunned)"** 상태로 둡니다. -하지만 적이 뭘 할지 모르면 전략을 짤 수 없습니다. - -**사용자의 의도:** "첫 턴에는 무조건 플레이어가 행동" -> 플레이어 선공 보장. -현재도 플레이어 선공입니다. 단지 적이 "방어 버프"를 두르고 있을 뿐. - -**수정 제안:** -`_prepareNextStage`에서 생성하는 첫 Intent는 **`applyDefense: false`**로 생성합니다. -그리고 `_enemyTurn`에서 **"만약 현재 Intent가 Defend이고 방어도가 아직 적용 안 됐다면(혹은 첫 턴이라면), 이때 방어도를 적용한다"**는 로직을 추가해야 합니다. - -하지만 코드가 복잡해집니다. -더 쉬운 방법: **첫 턴 Intent 생성 시, 아예 `Defend` 선택지를 배제합니다.** (첫 턴은 무조건 공격만 하도록). -이게 밸런스상으론 더 공격적일 수 있지만, "방어도가 적용되는 문제"는 확실히 해결됩니다. - -**사용자 멘트 재확인:** "스테이지 첫번째 enemyIntent 생성 시... isFirst 플래그를 추가해서 첫턴에는 무조건 플레이어가 행동하는걸로 해야해." --> 이 말은 **"적이 아무런 선제 액션(방어)도 취하지 않은 상태여야 한다"**는 뜻입니다. - -**전략:** -`BattleProvider`에 `isFirstTurn` 플래그를 추가하는 것이 아니라, `_generateEnemyIntent` 메서드에 `isFirstTurn` 파라미터를 추가합니다. -그리고 `isFirstTurn`이 `true`이면, **`Defend` 확률을 0으로 만들거나, `Defend`가 선택되어도 방어도를 적용하지 않고 넘깁니다.** -후자가 낫습니다. UI에는 "방어 예정"이라고 뜨고, 적 턴이 되었을 때 방어도가 올라가는 것이 가장 자연스럽습니다. - -**구현:** -1. `_generateEnemyIntent({bool applyImmediate = true})` -2. `_prepareNextStage` -> `_generateEnemyIntent(applyImmediate: false)` -3. `_enemyTurn` -> 만약 `currentEnemyIntent`가 `Defend`인데 방어도가 0이라면? (이미 깎였을 수도 있음). - -> **Flag 필요:** `bool _isPreemptiveDefenseApplied` 같은 변수가 필요할 수도 있습니다. - -아니면 단순히 **`_prepareNextStage`에서는 `Defend`가 안 나오게 막는 것**이 제일 깔끔합니다. (첫 턴은 공격만 함). -하지만 사용자가 "첫턴에는 무조건 플레이어가 행동"이라고 했지 "적은 공격만 해"라고 하진 않았습니다. - -**가장 적절한 구현:** -`_prepareNextStage`에서 Intent 생성 시 `applyImmediate: false`로 설정. -그리고 `_enemyTurn` 로직을 수정하여, **"Defend Intent인 경우, 기존에는 '이미 적용됨'이라 넘어갔지만, 이제는 '적용 안 됐으면 지금 적용'하도록"** 변경. - -어떻게 '적용 안 됐음'을 알까요? -`EnemyIntent` 클래스에 `bool applied` 필드를 추가하면 됩니다! - -**계획:** -1. `EnemyIntent` 클래스에 `bool isApplied` 필드 추가 (기본 false). -2. `_generateEnemyIntent`에서 `applyImmediate`가 true면 방어도 올리고 `isApplied = true`. -3. `_prepareNextStage`에서는 `applyImmediate: false`로 호출 -> `isApplied = false`. -4. `_enemyTurn`에서 `Intent`가 `Defend`일 때, `!isApplied`라면 방어도 적용하고 이펙트 띄움. - -이 방식이 완벽합니다. diff --git a/prompt/74_fix_item_name_interpolation.md b/prompt/74_fix_item_name_interpolation.md deleted file mode 100644 index 8c8e8ff..0000000 --- a/prompt/74_fix_item_name_interpolation.md +++ /dev/null @@ -1,12 +0,0 @@ -# 74. Fix Item Name Interpolation - -## 1. 문제 (Problem) -- 아이템 이름 생성 시 `Instance of 'ItemTemplate'.name` 형태의 잘못된 문자열이 출력됨. -- 원인은 Dart의 문자열 보간 문법 오류: `"$template.name"`은 객체를 문자열로 변환함. - -## 2. 해결 방안 (Solution) -- `lib/game/logic/loot_generator.dart` 파일 내의 문자열 보간 코드를 수정. -- `"${selectedModifier.prefix} $template.name"` -> `"${selectedModifier.prefix} ${template.name}"` - -## 3. 기대 효과 (Expected Outcome) -- 아이템 이름이 "Sharp Wooden Sword" 처럼 정상적으로 출력됨.