Phase 2 미구현 시스템 — 구현 계획
v1.0 | 2026-03-31
Phase 1~3 그래픽 정책: 모든 비주얼은 placeholder(텍스트 라벨 +
_draw()기본도형)로 구현. 실제 픽셀아트 에셋은 Phase 4에서 교체.
목차
1. 포획 시스템
설계 문 서:
docs/game/system/capture.md적 데이터:game/data/enemies.json(capture 블록 이미 존재)
1-1. 개요
- 봉인 부적 패시브 장착 → 적 처치 시 확률 포획 → 도감 등록 → 소환수 장착/발동
- enemies.json에 보스 capture 데이터는 이미 존재. 일반 적 capture 필드 추가 필요
1-2. 데이터 변경
enemies.json — 일반 적에 capture 블록 추가
각 일반 적에 다음 필드 추가 (보스는 이미 있음):
"imp": {
... (기존 필드),
"capture": {
"grade": "F",
"base_chance": 15,
"summon_id": "imp_shield",
"summon_effect": "shield",
"summon_duration": 5.0,
"summon_description": "아군 방패 (5초간 투사체 흡수)"
}
}
전체 일반 적 capture 데이터:
| 적 | grade | base_chance | summon_id | summon_effect | duration |
|---|---|---|---|---|---|
| imp | F | 15 | imp_shield | shield | 5.0 |
| dokkaebi_fire | F | 12 | fire_screen | screen_damage | 2.0 |
| egg_ghost | E | 10 | egg_vision | vision_expand | 10.0 |
| ghost_maiden | D | 8 | maiden_fear | fear | 3.0 |
| skeleton | D | 8 | skeleton_shield | shield | 5.0 |
| gangsi | C | 5 | gangsi_wall | invincible_wall | 3.0 |
| reaper | B | 3 | reaper_execute | execute | 1.0 |
| gumiho | A | 2 | fox_charm_small | charm | 5.0 |
| daegwi | A | 2 | daegwi_blast | screen_damage | 2.0 |
game/data/capture_codex.json (신규)
도감 달성 보상 및 천장 시스템 설정 데이터:
{
"codex": {
"total_entries": 12,
"rewards": {
"30": { "threshold": 4, "effect": "capture_chance_bonus", "value": 5 },
"50": { "threshold": 6, "effect": "summon_duration_bonus", "value": 3.0 },
"80": { "threshold": 10, "effect": "summon_power_multiplier", "value": 1.5 },
"100": { "threshold": 12, "effect": "unlock_gumiho_npc", "value": true }
}
},
"pity": {
"boss_pity_increment": 10,
"boss_pity_max": 70,
"normal_pity": false
},
"seal_talisman_bonus_per_level": 2,
"seal_talisman_max_level": 5,
"shrine_capture_bonus_per_level": 5,
"shrine_capture_max_level": 5
}
1-3. 신규 파일 목록
| 파일 경로 | 역할 |
|---|---|
game/src/systems/capture_system.gd | Autoload 싱글톤. 포획 판정/도감/천장 관리 |
game/src/systems/summon_system.gd | 소환수 장착/발동/쿨다운 관리 |
game/data/capture_codex.json | 도감 보상 + 천장 설정 데이터 |
game/src/ui/capture_popup.gd | 포획 성공/실패 팝업 UI |
game/src/ui/summon_button.gd | HUD 소환수 발동 버튼 |
game/test/systems/test_capture_system.gd | GdUnit4 테스트 |
game/test/systems/test_summon_system.gd | GdUnit4 테스트 |
1-4. 기존 파일 변경
| 파일 | 변경 내용 |
|---|---|
game/data/enemies.json | 일반 적 9종에 capture 블록 추가 |
game/src/autoloads/data_manager.gd | _file_map에 "capture_codex": "capture_codex.json" 추가 + get_capture_codex() 메서드 |
game/src/autoloads/event_bus.gd | 시그널 추가: enemy_captured(enemy_id, position), capture_failed(enemy_id, position), boss_capture_attempt(boss_id), summon_activated(summon_id) |
game/src/enemies/enemy_base.gd | _on_death() 에서 봉인 부적 보유 시 CaptureSystem.try_capture() 호출 |
game/src/systems/boss_spawn_system.gd | 보스 HP 10% 이하 시 EventBus.boss_capture_attempt emit |
game/src/ui/hud.gd | 소환수 버튼 영역(우하단 24x24) 추가 |
project.godot | Autoload 등록: CaptureSystem, SummonSystem |
1-5. capture_system.gd 핵심 구조
CaptureSystem (Autoload)
├── var codex: Dictionary = {} # {enemy_id: bool} 포획 여부
├── var boss_pity: Dictionary = {} # {boss_id: int} 천장 누적
├── var seal_talisman_level: int = 0 # 봉인 부적 레벨
│
├── func try_capture(enemy_id, enemy_data, position) -> bool
│ ├── 봉인 부적 미보유 → return false
│ ├── 최종 확률 계산 (기본 + 부적레벨 + 사당 + 도감보상 + 천장)
│ ├── randf() < 확률 → 성공 → _register_codex() + emit captured
│ └── 실패 → emit failed (보스는 천장 누적)
│
├── func try_boss_capture(boss_id, boss_data, position) -> bool
│ ├── 보스 전용 연출 로직 (슬로우 등은 Coder가 구현)
│ └── 성공/실패 + 천장 업데이트
│
├── func get_codex_completion() -> float
├── func get_codex_rewards() -> Array[Dictionary]
└── func _calculate_capture_chance(base, enemy_grade) -> float
1-6. summon_system.gd 핵심 구조
SummonSystem (Autoload)
├── var equipped_summons: Array[String] = [] # 장착 소환수 ID 목록
├── var max_slots: int = 1 # 사당 업그레이드로 최대 6
├── var used_this_run: Array[String] = [] # 이번 판 사용 여부
│
├── func equip_summon(summon_id) -> bool
├── func activate_summon(slot_index: int) -> void
│ ├── 이미 사용 → return
│ ├── 소환수 효과 발동 (효과별 분기)
│ └── used_this_run에 추가
│
├── func reset_for_new_run() -> void
└── func get_available_summons() -> Array[String]
1-7. placeholder 비주얼
| 요소 | placeholder 구현 |
|---|---|
| 포획 성공 이펙트 | 적색 원(draw_circle) + "포획!" 텍스트 Label |
| 포획 실패 이펙트 | 회색 원 + "실패..." 텍스트 |
| 보스 봉인 연출 | 화면 슬로우(Engine.time_scale=0.3) + "봉인!" 대형 텍스트 |
| 소환수 발동 | 소환수 이름 텍스트 + 효과 범위 원 표시 |
| HUD 소환수 버튼 | ColorRect(24x24, 적색) + 텍스트("召") |
| 도감 UI | 6x2 그리드 ColorRect + 적 이름/??/체크 텍스트 |
1-8. 구현 순서
- enemies.json에 capture 블록 추가 + capture_codex.json 생성
- CaptureSystem 싱글톤 (포획 판정 + 도감)
- enemy_base.gd 사망 시 포획 판정 연결
- 보스 포획 (HP 10% 트리거 + 슬로우 연출)
- SummonSystem (장착 + 발동)
- HUD 소환수 버튼
- 포획 팝업 UI
- GdUnit4 테스트
2. 저주 시스템
설계 문서:
docs/game/system/curses.md데이터:game/data/curses.json(미생성 — 신규 생성 필요)
2-1. 개요
- 판 시작 전 0~3개 저주 선택 → 난이도 상승 + 혼백 배율 증가
- 6종 저주, 판 수 기반 점진적 해금
- curses.json 데이터 파일 필요
2-2. 데이터 — game/data/curses.json (신규)
{
"_meta": {
"version": "1.0.0",
"description": "저주 6종 데이터. 판 시작 전 선택, 혼백 배율 곱연산."
},
"max_active_curses": 3,
"base_honbaek_per_clear": 10,
"base_honbaek_per_death": 5,
"ilv_bonus_per_curse": 0.5,
"curses": {
"blood_moon": {
"name_kr": "혈월",
"description": "모든 적 HP +50%",
"icon_text": "月",
"honbaek_multiplier": 1.5,
"difficulty_stars": 3,
"unlock_condition": "runs_cleared",
"unlock_value": 10,
"effect": {
"type": "enemy_hp_multiplier",
"value": 1.5,
"target": "all_enemies"
},
"visual_effect": "red_moon_overlay"
},
"ghost_eye": {
"name_kr": "귀안",
"description": "시야 반경 -30%",
"icon_text": "眼",
"honbaek_multiplier": 1.3,
"difficulty_stars": 2,
"unlock_condition": "runs_cleared",
"unlock_value": 10,
"effect": {
"type": "vision_reduction",
"value": 0.3,
"target": "player"
},
"visual_effect": "vignette_overlay"
},
"seal": {
"name_kr": "봉인",
"description": "무기 슬롯 -1 (최대 3개)",
"icon_text": "封",
"honbaek_multiplier": 1.5,
"difficulty_stars": 4,
"unlock_condition": "runs_cleared",
"unlock_value": 15,
"effect": {
"type": "weapon_slot_reduction",
"value": 1,
"target": "player"
},
"visual_effect": "weapon_slot_x"
},
"regression": {
"name_kr": "역행",
"description": "경험치 -30%",
"icon_text": "逆",
"honbaek_multiplier": 1.4,
"difficulty_stars": 3,
"unlock_condition": "runs_cleared",
"unlock_value": 15,
"effect": {
"type": "xp_reduction",
"value": 0.3,
"target": "player_and_soul"
},
"visual_effect": "xp_bar_gray"
},
"impermanence": {
"name_kr": "무상",
"description": "레벨업 선택지 3->2",
"icon_text": "無",
"honbaek_multiplier": 1.3,
"difficulty_stars": 2,
"unlock_condition": "runs_cleared",
"unlock_value": 20,
"effect": {
"type": "levelup_choice_reduction",
"value": 1,
"target": "levelup_system"
},
"visual_effect": "none"
},
"vengeful_spirit": {
"name_kr": "원혼",
"description": "죽은 적 30% 확률 부활 (HP 50%)",
"icon_text": "怨",
"honbaek_multiplier": 2.0,
"difficulty_stars": 5,
"unlock_condition": "all_curses_used_once",
"unlock_value": true,
"effect": {
"type": "enemy_revive",
"revive_chance": 0.3,
"revive_hp_ratio": 0.5,
"target": "all_enemies"
},
"visual_effect": "white_smoke_on_death"
}
},
"hidden_reward": {
"condition": "all_curses_unlocked_plus_3curse_clear",
"reward": "unlock_character_amhaeng_eosa"
}
}
2-3. 신규 파일 목록
| 파일 경로 | 역할 |
|---|---|
game/data/curses.json | 저주 6종 데이터 |
game/src/systems/curse_system.gd | Autoload 싱글톤. 저주 해금/선택/효과 적용 |
game/src/ui/curse_selection_screen.gd | 판 시작 전 저주 선택 UI |
game/test/systems/test_curse_system.gd | GdUnit4 테스트 |
2-4. 기존 파일 변경
| 파일 | 변경 내용 |
|---|---|
game/src/autoloads/data_manager.gd | _file_map에 "curses": "curses.json" 추가 + get_curse(id), get_all_curses() 메서드 |
game/src/autoloads/event_bus.gd | 시그널 추가: curses_selected(curse_ids: Array), honbaek_earned(amount: int, multiplier: float) |
game/src/autoloads/game_manager.gd | run_data["curses"] 이미 존재. run_data에 "honbaek_multiplier": 1.0 추가 |
game/src/systems/level_up_system.gd | 저주 "무상" 활성 시 선택지 3→2 반영 |
game/src/enemies/enemy_base.gd | 저주 "혈월" 적용: 스폰 시 HP *= 1.5 |
game/src/enemies/enemy_base.gd | 저주 "원혼" 적용: 사망 시 30% 확률 부활 (HP 50%) |
game/src/systems/game_world.gd | 저주 "귀안" 시야 축소 비네팅 오버레이 |
game/src/ui/hud.gd | 우상단에 활성 저주 아이콘 표시 (최대 3개) |
project.godot | Autoload 등록: CurseSystem |
2-5. curse_system.gd 핵심 구조
CurseSystem (Autoload)
├── var unlocked_curses: Array[String] = [] # 해금된 저주 ID
├── var active_curses: Array[String] = [] # 현재 판 활성 저주
├── var curse_usage_history: Dictionary = {} # {curse_id: int} 사용 횟수
├── var total_runs_cleared: int = 0 # 총 클리어 판 수
│
├── func check_unlocks() -> Array[String] # 조건 만족 저주 해금
├── func select_curses(curse_ids: Array) -> void # 판 시작 전 선택
│ ├── 최대 3개 검증
│ └── active_curses 설정 + emit curses_selected
│
├── func get_honbaek_multiplier() -> float # 곱연산 배율 반환
├── func get_ilv_bonus() -> float # 저주 수 * 0.5
│
├── func is_curse_active(curse_id: String) -> bool
├── func apply_enemy_hp_modifier(base_hp: float) -> float # 혈월
├── func get_levelup_choices() -> int # 무상: 3→2
├── func get_xp_multiplier() -> float # 역행: 0.7
├── func should_enemy_revive() -> bool # 원혼: 30%
├── func get_weapon_slot_count() -> int # 봉인: 4→3
└── func get_vision_reduction() -> float # 귀안: 0.3
2-6. 저주 선택 UI (curse_selection_screen.gd)
placeholder 구현:
- CanvasLayer (거점→스테이지 사이 표시)
- 6개 카드 (3x2 그리드, ColorRect + 텍스트)
- 잠금 카드: 회색 + 해금 조건 텍스트
- 선택 카드: 황금 테두리 + "저주 X/3" 카운터
- 우측: 현재 혼백 배수 텍스트
- "출격!" / "저주 없이 시작" 버튼
2-7. 저주별 구현 난이도
| 저주 | 구현 복잡도 | 설명 |
|---|---|---|
| 혈월 | 낮음 | enemy_base 스폰 시 HP 곱 |
| 귀안 | 중간 | 비네팅 셰이더/오버레이 |
| 봉인 | 낮음 | 무기 슬롯 상수 변경 |
| 역행 | 낮음 | XP 획득 곱 |
| 무상 | 낮음 | level_up_system 선택지 수 변경 |
| 원혼 | 중간 | 적 사망→부활 로직 + ICD |
2-8. 구현 순서
- curses.json 데이터 파일 생성
- CurseSystem 싱글톤 (해금/선택/효과 조회)
- curse_selection_screen.gd (저주 선택 UI)
- 각 저주 효과를 기존 시스템에 연결:
- 혈월 → enemy_base.gd
- 봉인 → game_manager.gd / weapon slot
- 역행 → XP 시스템
- 무상 → level_up_system.gd
- 귀안 → game_world.gd (비네팅)
- 원혼 → enemy_base.gd (부활)
- HUD 저주 아이콘 표시
- GdUnit4 테스트
3. NPC/거점 시스템
설계 문서:
docs/game/system/npc-hub.md기존 코드:game/src/hub/hub_screen.gd(스캐폴딩만)
3-1. 개요
현재 hub_screen.gd에는 NPC 카드 4종이 "(준비 중)" 상태로 표시. 이를 실제 상호작용 가능한 NPC 시스템으로 확장.
Phase 2 구현 범위:
- 할멈 (힌트 대사) — 간소화: 랜덤 대사 표시
- 대장장이 (장비 강화/재련) — 핵심 기능
- 저승사자 (도감 열람) — 포획 시스템 연동
- 사당 (영구 업그레이드) — 핵심 기능
- 구미호 (히든) — Phase 3으로 연기
3-2. 신규 파일 목록
| 파일 경로 | 역할 |
|---|---|
game/src/hub/npc_dialog.gd | NPC 대화 프레임 (공통) |
game/src/hub/npc_grandma.gd | 할멈 — 힌트 대사 |
game/src/hub/npc_blacksmith.gd | 대장장이 — 장비 강화/재련 |
game/src/hub/npc_reaper.gd | 저승사자 — 도감 열람 |
game/src/hub/npc_shrine.gd | 사당 — 영구 업그레이드 |
game/src/hub/stage_select_screen.gd | 스테이지 선택 화면 |
game/data/shrine.json | 사당 업그레이드 11종 데이터 |
game/data/npc_dialogs.json | 할멈 대사 데이터 |
game/src/autoloads/save_manager.gd | 영구 데이터 저장/로드 (엽전, 혼백, 사당, 도감, 저주 해금) |
game/test/hub/test_npc_shrine.gd | GdUnit4 테스트 |
game/test/hub/test_npc_blacksmith.gd | GdUnit4 테스트 |
3-3. 기존 파일 변경
| 파일 | 변경 내용 |
|---|---|
game/src/hub/hub_screen.gd | NPC 카드 클릭 → 각 NPC 씬/UI 진입. "(준비 중)" 제거. 하단에 엽전/혼백 표시. "스테이지 시작" → 스테이지 선택으로 변경. 영혼 선택/저주 선택 플로우 추가 |
game/src/autoloads/data_manager.gd | _file_map에 "shrine": "shrine.json", "npc_dialogs": "npc_dialogs.json" 추가 |
game/src/autoloads/event_bus.gd | 시그널 추가: npc_interacted(npc_id), equipment_upgraded(result), shrine_upgraded(upgrade_id, new_level), stage_selected(stage_id), soul_selected(soul_id) |
game/src/autoloads/game_manager.gd | State.HUB 시 거점 데이터 로드. run_data에 "selected_stage": "" 추가 |
project.godot | Autoload 등록: SaveManager |
3-4. game/data/shrine.json (신규)
{
"_meta": { "version": "1.0.0", "description": "사당 영구 업그레이드 11종" },
"upgrades": {
"health": { "name_kr": "체력", "icon_text": "体", "max_level": 10, "effect_per_level": 5, "unit": "HP", "cost_formula": "20 * level" },
"attack": { "name_kr": "공격력", "icon_text": "攻", "max_level": 10, "effect_per_level": 3, "unit": "%", "cost_formula": "25 * level" },
"move_speed": { "name_kr": "이동속도", "icon_text": "速", "max_level": 10, "effect_per_level": 2, "unit": "%", "cost_formula": "20 * level" },
"cooldown": { "name_kr": "쿨다운", "icon_text": "冷", "max_level": 10, "effect_per_level": -2, "unit": "%", "cost_formula": "30 * level" },
"xp_gain": { "name_kr": "경험치", "icon_text": "経", "max_level": 10, "effect_per_level": 5, "unit": "%", "cost_formula": "15 * level" },
"coin_gain": { "name_kr": "엽전 획득", "icon_text": "銭", "max_level": 10, "effect_per_level": 5, "unit": "%", "cost_formula": "20 * level" },
"revive": { "name_kr": "부활", "icon_text": "復", "max_level": 3, "effect_per_level": 1, "unit": "회", "cost_formula": "100 * level" },
"start_weapon": { "name_kr": "시작 무기", "icon_text": "武", "max_level": 1, "effect_per_level": 1, "unit": "슬롯", "cost_formula": "500" },
"capture_slot": { "name_kr": "포획 용량", "icon_text": "捕", "max_level": 5, "effect_per_level": 1, "unit": "슬롯", "cost_formula": "50 * level" },
"yinyang_stable": { "name_kr": "음양 안정", "icon_text": "陰", "max_level": 5, "effect_per_level": -0.1, "unit": "/초", "cost_formula": "40 * level" },
"capture_boost": { "name_kr": "포획 강화", "icon_text": "封", "max_level": 5, "effect_per_level": 5, "unit": "%", "cost_formula": "60 * level" }
}
}
3-5. save_manager.gd 핵심 구조
SaveManager (Autoload)
├── const SAVE_PATH = "user://save_data.json"
├── var data: Dictionary = {}
│ ├── "coins": int # 엽전
│ ├── "honbaek": int # 혼백
│ ├── "shrine_levels": {} # {upgrade_id: int}
│ ├── "codex": {} # {enemy_id: bool}
│ ├── "boss_pity": {} # {boss_id: int}
│ ├── "curse_unlocks": [] # 해금된 저주 ID
│ ├── "curse_usage": {} # {curse_id: int}
│ ├── "runs_cleared": int
│ ├── "runs_total": int
│ ├── "equipped_summons": []
│ └── "character_unlocks": []
│
├── func save() -> void
├── func load_save() -> void
├── func get_coins() -> int
├── func spend_coins(amount: int) -> bool
├── func add_coins(amount: int) -> void
├── func get_honbaek() -> int
├── func add_honbaek(amount: int) -> void
├── func spend_honbaek(amount: int) -> bool
├── func get_shrine_level(id: String) -> int