BurningTimesAi/프로젝트/수상한잡화점/개발/08_전투시스템_SOT_v1.md

404 lines
18 KiB
Markdown
Raw Normal View History

# 08. 전투시스템 SOT (Single Source of Truth) v1
- 작성일: 2026-04-14
- 작성자: 개발실장 (Explore 에이전트 분석 위임)
- 목적: 기획실 밸런싱·시뮬레이터 검증을 지원하기 위해 **코드가 실제로 수행하는 전투 로직**을 SOT로 확정
- 스코프: `D:/NerdNavis/FilGoodBandits/DeckBuilding/Assets/Script/` 전수
- 원칙: 코드가 말하는 것만 기록. 추측 금지. 불확실한 항목은 **(확인 필요)** 태그 명시
---
## 1. 전투 시스템의 파일 맵
### 1.1 핵심 매니저 및 컨트롤러
| 파일경로 | 클래스명 | 역할 |
|---|---|---|
| `Assets/Script/InGame/Stage/MonsterNodeControler.cs` | `MonsterNodeControler` | **전투 루프 제어 중심** — 몬스터 배치, 라인 변경, 노드 종료 |
| `Assets/Script/InGame/Actor/Actor.cs` | `Actor` (기본 클래스) | 피해 계산, 회피, 크리티컬, 상태이상 (약 3,700줄) |
| `Assets/Script/InGame/Actor/PCActor.cs` | `PCActor` | 플레이어 전용 — 방어 입력, HUD 관리 |
| `Assets/Script/InGame/Actor/MobActor.cs` | `MobActor` | 몬스터 전용 — 라인 위치, 드롭 보상 |
### 1.2 핵심 데이터 타입
| 파일:라인 | 타입 | 설명 |
|---|---|---|
| `My/MyClass.cs:339` | `ProjectileData` | 발사체/공격 정보 (데미지, 크리, 특수효과) |
| `My/MyValue.cs:517` | `ActorStatInfo` | 배우 스탯 (HP, 공격력, 회피율 등) |
| `InGame/Stage/IngameStageData.cs:63` | `StageNodeData` | 노드 데이터 (몬스터 구성) |
| `Table/Tables/table_monsterlist.cs:8` | `MonsterListTableData` | 몬스터 테이블 (Normal/Boss 구분) |
### 1.3 전투 씬 진입/종료 경로
- **진입**: `MonsterNodeControler.Set(endnode, stagedata)` (`MonsterNodeControler.cs:42`)
- 몬스터 생성: `MonsterNodeControler.cs:92~118`
- 배우 초기화: `MonsterNodeControler.cs:142~168`
- 전투 시작: `MonsterNodeControler.cs:170``InGameInfo.Ins.BattleStart_AfterMakeMob()`
- **종료**:
- 몬스터 전멸: `Co_AllKill()` (`MonsterNodeControler.cs:334~343`)
- 라인 변경: `Co_LineChange()` (`MonsterNodeControler.cs:362~450`)
---
## 2. 전투 라이프사이클
### 2.1 흐름
```
1. 노드 진입: MonsterNodeControler.Set(endnode, stagedata)
2. 패턴 선택: table_MonsterPatternList (PatternID 기반)
3. 몬스터 동적 생성: Get_MakeMob_Dynamic() [cs:198~255]
- 라인(전열/중열/대기열) × 슬롯(3) = 9슬롯
- 슬롯별 근접/원거리/고유 확률 추첨
4. 배우 배치: Actor 9개 (0~8: 전열 3, 중열 3, 대기열 3)
5. 전투 루프: Actor.Update() [Actor.cs:79~111]
- AttackCoolTime 감소 → 0 도달 시 Play_Attack()
- 상태이상·카드 효과 업데이트
6. 공격 발동: Actor.Shoot_Projectile() [Actor.cs:946~1039]
7. 피해 계산: Actor.Get_Dmg(ProjectileData) [Actor.cs:521~834]
8. 라인 전멸 체크: Check_LineAllDead() [Actor.cs:480]
- 라인 변경: Co_LineChange()
- 전원 처치: Co_AllKill() → act_EndNode()
```
### 2.2 주요 이벤트 / 콜백
| 이벤트 | 함수 | 시점 |
|---|---|---|
| 피해 수신 | `Actor.Get_Dmg(ProjectileData)` `Actor.cs:521` | 발사체 도달 |
| 회피 판정 | `Run_AvoidStatus()` `Actor.cs:1640` | 회피 성공 |
| 크리티컬 결정 | `ProjectileData.Set()` `MyClass.cs:354` | 발사체 생성 |
| 사망 | `Actor.Set_Die(ProjectileData)` `Actor.cs:876` | HP ≤ 0 |
| 부활 | `Co_Resurrection()` `Actor.cs:926` | 부활 카드/성소 |
| 라인 변경 | `Co_LineChange()` `MonsterNodeControler.cs:362` | 라인 전멸 |
| 노드 종료 | `act_EndNode()` `MonsterNodeControler.cs:456` | 전원 처치 |
---
## 3. 공격/피해 계산 수식
### 3.1 기본 공격력
`MyValue.cs:800~804`
```csharp
public int Get_Damage()
{
var dmg = Random.Range((int)Get_TotalStat(eStat.Attack_Min),
(int)Get_TotalStat(eStat.Attack_Max) + 1);
return Mathf.Max(dmg, 1); // 최소 1 보장
}
```
**Attack_Min 최종값** (`MyValue.cs:585~603`)
```csharp
case eStat.Attack_Min:
{
rtn += Get_Stat(eStat.Attack) + Get_BuffStat(eStat.Attack) - Get_DebuffStat(eStat.Attack);
// G5_AttackUpBySkillCardCount: 스킬카드 수만큼 증가
if (m_Actor.IsObtainCardSkill(cardtype))
rtn += m_Actor.Get_SkillCardCount();
// G5_FlatDamageMinMaxEqual: 최소/최대 동일화
if (m_Actor.IsObtainCardSkill(cardtype))
{
var maxdmg = Get_Stat(eStat.Attack_Max) + Get_BuffStat(eStat.Attack_Max)
- Get_DebuffStat(eStat.Attack_Max);
rtn = Mathf.Max((int)rtn, (int)maxdmg);
}
var mul = Get_BuffStat(eStat.DmgMul)
+ Get_BuffStat(eStat.Attack_Min_Mul)
+ Get_BuffStat(eStat.PC5_DmgMul);
return (int)(rtn + (rtn * mul));
}
```
**기여도 순서** (카드 > 장비 > 각성 > 인장 원칙과 매핑):
1. 기본값: `ActorTableDataBase.n_AttackMin / n_AttackMax`
2. 장비: `ApplyMainStat()` / `ApplyStat()` (`MyValue.cs:261~282`)
3. 각성: `eAwakening.Stat_ADD`, `eUniqueAwakeningType.PC1_UniqueAwakening1~5` (`MyValue.cs:156~217`)
4. 인장: `eSpecialEffect.CritChance`, `CritDamage` 등 (`MyValue.cs:145~154`)
5. 런 내 버프/디버프: `Set_Buff()` / `Set_Debuff()` (`MyValue.cs:747~792`)
### 3.2 최종 피해 계산 (15단계)
`Actor.cs:521~834` 순서대로 요약.
1. **기본**: `baseDmg = hitterstat.Get_Damage()`
2. **1회성 절대 증가**: `AddDmg_1Time` + 인장 절대값
3. **1회성 배수 증가**: `AddDmgMul_1Time` + 인장 배수
4. **스턴/라인 보정**: `DmgMul_byStun`, `FrontLine_Dmg_Mul_1Time`, `MiddleLine_Dmg_Mul_1Time`, `BackLine_Dmg_Mul_1Time`
5. **G3_PotionNextAttackBoost**: 물약 스택 소모 시 배수 추가
6. **G5_FirstAttackTripleDamage**: 첫 공격이면 배수를 해당 값으로 **대체**(`Actor.cs:719~724`)
7. **곱연산 합산**: `hitter_dmg = base + 절대 + (hitter_dmg * 배수)` (`Actor.cs:726`)
8. **크리티컬**: `hitter_dmg *= CriDmg` (크리 시) (`Actor.cs:758`)
9. **절대 감소**: `ReduceMeeleDmg` / `ReduceRangeDmg` / `ReduceDmg` + 방어 중이면 `PCDefence` (`Actor.cs:776`)
10. **비율 감소**: `ReduceMeeleDmg_Mul` / `ReduceRangeDmg_Mul` + 방어 중이면 `PCDefence_Mul`, `G2_ReduceRangedDamageWithShield` (`Actor.cs:808`)
11. **최소 피해 1**: `hitter_dmg = max(hitter_dmg, 1)` (`Actor.cs:811`)
12. **G4_ShieldedDamageReduction**: 쉴드 보유 시 비율 추가 감소
13. **FixedDmg**: 고정 피해 스탯이 있으면 피해를 해당 값으로 **대체** (`Actor.cs:817`)
14. **G2_StackDamageOnSameTarget**: 동일 대상 연타 스택 추가 피해 (`Actor.cs:826`)
15. **쉴드 우선 감산****HP 감산** (`Actor.cs:435`)
**요약식**
```
기본 = Random[Attack_Min, Attack_Max]
중간 = 기본 + 절대증가 + (기본 × 배수증가)
(크리 시) 중간 × CriDmg
→ 중간 - 절대감소
→ 중간 - (중간 × 감소비율)
→ max(중간, 1)
→ (쉴드 존재) 쉴드 먼저 흡수 → HP 감산
```
### 3.3 크리티컬
`MyClass.cs:354~393`
```csharp
isCri = DSUtil.RandomTrue(crirate); // 1차: 확률 판정
if (Hitter.IsObtainCardSkill(G2_NextSevenHitsCritical)) // 2차: N연타 강제
{
var card = Hitter.Get_CardSkillData_orNull(cardtype);
if (card.UseCount_Acc < card.m_Data.Get_IntValue1())
{
++card.UseCount_Acc;
isCri = true;
}
}
```
**크리 확률 상한**: `MaxCri = 0.9f` (`MyValue.cs:23`).
**크리 배수 적용**: `hitter_dmg *= Get_TotalStat(eStat.CriDmg)` (`Actor.cs:758`).
### 3.4 회피 판정 (3단계)
**1단계 — 명중률 체크 (PC 한정)** (`Actor.cs:623`)
```csharp
isHit = DSUtil.RandomTrue(hitterstat.Get_TotalStat(eStat.HitRate) / (avoid * 3.69));
```
> **(확인 필요)** 3.69 상수의 근거.
**2단계 — PC 회피율** (`Actor.cs:633~647`)
```csharp
if (DSUtil.RandomTrue(Attack가_Melee ? avoid_melee : avoid_range))
Run_AvoidStatus(...); return;
```
**3단계 — 최초 공격 무조건 회피** (`Actor.cs:649~669`)
- 근접 첫 공격: `G2_FirstMeleeAlwaysEvade`
- 원거리 첫 공격: `G3_DodgeFirstRangedAttack`
**회피율 상한**: `MaxAvoid = 0.9f` (`MyValue.cs:23`) — **PC 한정**. 몬스터 회피는 정수형(비일관성 리스크).
### 3.5 상태이상
`Actor.cs:512~513`
```csharp
OnEvent_GetDmg(pdData);
Set_StatusEffect(pdData.Get_SoSData(pdData.Hitter), this);
```
상태이상 데이터는 `table_StatusOptionSet`에서 조회 (`MyClass.cs:401~438`). 업데이트 불가 CC(Stun/Freezing 등) 은 `Actor.cs:52~53` 상수 참조.
---
refactor(rules): 원칙 3 개정 + A+B급 6건 조직 룰 최적화 집행 ## PD님 승인 범위 - 2026-04-18 원칙 3 개정 (저장 위치 최적화 반영) - 2026-04-17 저녁 "옵션 나" A+B급 7건 일괄 승인 ## 수정 3대 원칙 (개정 반영) 1. 자산 보존 분리 — 배너·아카이브 섹션·외부 파일 2. sed 일괄 치환 금지 — 수동 정밀 치환 3. 폐기 선언 자산 유지 + 저장 위치 최적화 (신) — 활성 본문 1줄 + 외부 아카이브 파일 ## Phase 1 원칙 3 개정 - 공유/조직공지/폐기_규칙_아카이브.md 신설 (P20·C17·구 C18·구 C24·구 C26 5종 이관) - 인계서 §1 원칙 3 전면 개정 ## Phase 2 A+B급 집행 (6건 + PM 재량 확대) - A1: CLAUDE.md L33~49 C1~C31/P1~P28 체계 갱신 + 폐기 규칙 1줄 압축 - A2: SKILL.md 17+ 개소 5단계 chunk 분할 정리 (활성 운영 지침 P20 참조 0건 달성) - A3: 클라이언트·서버팀장 P20 → P24·C27·C29-4·C30·3축 감사 - A3 확장 (PM 재량): pm-general·개발팀장·기획팀장 frontmatter 갱신 - B1: 07 Headless 원안 아카이브 배너 + 02_점검 L19 주해 - B2: 02 추출대상 완료 실적 배너 + Tier 1 16/16 구현 경로 역참조 - B3: 08 전투시스템 SOT Q-P2 실측 수치 반영 (#37, PCDefence_Mul=0.3) - B4: 집행 불요 (실측 결과 이미 해결 상태) ## 검증 - verify_log_paths.sh 4건 실존 유지 - verify_references.sh 신규 파손 0건 - .live/ 7건 동기 기록 (P25 실증) - 대화로그 2026-04-18 엔트리 (기각안 필수 필드 포함) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:54:32 +00:00
## 4. 터치 방어 메커닉 (Q-P1 → Q-P2)
### 4.1 결론
refactor(rules): 원칙 3 개정 + A+B급 6건 조직 룰 최적화 집행 ## PD님 승인 범위 - 2026-04-18 원칙 3 개정 (저장 위치 최적화 반영) - 2026-04-17 저녁 "옵션 나" A+B급 7건 일괄 승인 ## 수정 3대 원칙 (개정 반영) 1. 자산 보존 분리 — 배너·아카이브 섹션·외부 파일 2. sed 일괄 치환 금지 — 수동 정밀 치환 3. 폐기 선언 자산 유지 + 저장 위치 최적화 (신) — 활성 본문 1줄 + 외부 아카이브 파일 ## Phase 1 원칙 3 개정 - 공유/조직공지/폐기_규칙_아카이브.md 신설 (P20·C17·구 C18·구 C24·구 C26 5종 이관) - 인계서 §1 원칙 3 전면 개정 ## Phase 2 A+B급 집행 (6건 + PM 재량 확대) - A1: CLAUDE.md L33~49 C1~C31/P1~P28 체계 갱신 + 폐기 규칙 1줄 압축 - A2: SKILL.md 17+ 개소 5단계 chunk 분할 정리 (활성 운영 지침 P20 참조 0건 달성) - A3: 클라이언트·서버팀장 P20 → P24·C27·C29-4·C30·3축 감사 - A3 확장 (PM 재량): pm-general·개발팀장·기획팀장 frontmatter 갱신 - B1: 07 Headless 원안 아카이브 배너 + 02_점검 L19 주해 - B2: 02 추출대상 완료 실적 배너 + Tier 1 16/16 구현 경로 역참조 - B3: 08 전투시스템 SOT Q-P2 실측 수치 반영 (#37, PCDefence_Mul=0.3) - B4: 집행 불요 (실측 결과 이미 해결 상태) ## 검증 - verify_log_paths.sh 4건 실존 유지 - verify_references.sh 신규 파손 0건 - .live/ 7건 동기 기록 (P25 실증) - 대화로그 2026-04-18 엔트리 (기각안 필수 필드 포함) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:54:32 +00:00
**플레이어 터치 입력으로 방어가 발동된다 — 그러나 현재 확인된 경로는 "타겟팅"이다.** 명시적 방어 입력 윈도우는 ~~(확인 필요)~~**✅ 실측 확정 (2026-04-17 #37 Q-P2 정밀 2차)**: `UITouchHandler.cs` `IPointerDownHandler`·`IPointerUpHandler`·`IPointerExitHandler` 인터페이스로 구현. `IngameUIManager.cs:39` `m_PCDefence.Set(m_PCActor.Play_Defence, m_PCActor.Release_Defence, null)` 바인딩. **터치 Down↔Up 구간 동안 지속되는 상태 효과**. 근거: [`공유/소통/완료/2026-04-17_Phase0-C_QP2_정밀2차_응답서.md`](../../../공유/소통/완료/2026-04-17_Phase0-C_QP2_정밀2차_응답서.md).
### 4.2 구현
`MonsterNodeControler.cs:263~290` — 마우스/터치 다운 이벤트
```csharp
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// ... RaycastHit2D로 Actor 탐색
if (actor != null && !pc.IsDead() && targetlines && !actor.IsDead())
InGameInfo.Ins.m_PCActor.Set_Target(actor); // 타겟 변경
}
}
```
### 4.3 방어 상태 관련 흔적
- `PCActor.Play_Defence()` `PCActor.cs:148~154`: 방어 애니메이션 실행
- `PCActor.Release_Defence()` `PCActor.cs:155~161`: 방어 해제
refactor(rules): 원칙 3 개정 + A+B급 6건 조직 룰 최적화 집행 ## PD님 승인 범위 - 2026-04-18 원칙 3 개정 (저장 위치 최적화 반영) - 2026-04-17 저녁 "옵션 나" A+B급 7건 일괄 승인 ## 수정 3대 원칙 (개정 반영) 1. 자산 보존 분리 — 배너·아카이브 섹션·외부 파일 2. sed 일괄 치환 금지 — 수동 정밀 치환 3. 폐기 선언 자산 유지 + 저장 위치 최적화 (신) — 활성 본문 1줄 + 외부 아카이브 파일 ## Phase 1 원칙 3 개정 - 공유/조직공지/폐기_규칙_아카이브.md 신설 (P20·C17·구 C18·구 C24·구 C26 5종 이관) - 인계서 §1 원칙 3 전면 개정 ## Phase 2 A+B급 집행 (6건 + PM 재량 확대) - A1: CLAUDE.md L33~49 C1~C31/P1~P28 체계 갱신 + 폐기 규칙 1줄 압축 - A2: SKILL.md 17+ 개소 5단계 chunk 분할 정리 (활성 운영 지침 P20 참조 0건 달성) - A3: 클라이언트·서버팀장 P20 → P24·C27·C29-4·C30·3축 감사 - A3 확장 (PM 재량): pm-general·개발팀장·기획팀장 frontmatter 갱신 - B1: 07 Headless 원안 아카이브 배너 + 02_점검 L19 주해 - B2: 02 추출대상 완료 실적 배너 + Tier 1 16/16 구현 경로 역참조 - B3: 08 전투시스템 SOT Q-P2 실측 수치 반영 (#37, PCDefence_Mul=0.3) - B4: 집행 불요 (실측 결과 이미 해결 상태) ## 검증 - verify_log_paths.sh 4건 실존 유지 - verify_references.sh 신규 파손 0건 - .live/ 7건 동기 기록 (P25 실증) - 대화로그 2026-04-18 엔트리 (기각안 필수 필드 포함) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:54:32 +00:00
- 실제 호출부 ~~(확인 필요)~~**✅ 실측 확정 (#37)**: `IngameUIManager.cs:39``m_PCDefence.Set(...)` 으로 `UITouchHandler` 이벤트가 `Play_Defence`/`Release_Defence` 에 바인딩. `Update()`에서 `m_onHold?.Invoke()` 지속 호출 (현재 no-op).
### 4.4 방어 성공 효과 (`Actor.cs:515~519`)
```csharp
if (ActorStatus == eActorStatus.Defecne)
{
huddmg.Set_Block(dmg, Get_World_Position());
EffectMgr.Ins.Show_Effect("PCBlock", Get_World_Position(), this);
}
```
방어 중이면 피해 감소:
- 절대: `PCDefence` (`Actor.cs:775`, `table_GlobalValue`)
- 비율: `PCDefence_Mul` (`Actor.cs:783`)
refactor(rules): 원칙 3 개정 + A+B급 6건 조직 룰 최적화 집행 ## PD님 승인 범위 - 2026-04-18 원칙 3 개정 (저장 위치 최적화 반영) - 2026-04-17 저녁 "옵션 나" A+B급 7건 일괄 승인 ## 수정 3대 원칙 (개정 반영) 1. 자산 보존 분리 — 배너·아카이브 섹션·외부 파일 2. sed 일괄 치환 금지 — 수동 정밀 치환 3. 폐기 선언 자산 유지 + 저장 위치 최적화 (신) — 활성 본문 1줄 + 외부 아카이브 파일 ## Phase 1 원칙 3 개정 - 공유/조직공지/폐기_규칙_아카이브.md 신설 (P20·C17·구 C18·구 C24·구 C26 5종 이관) - 인계서 §1 원칙 3 전면 개정 ## Phase 2 A+B급 집행 (6건 + PM 재량 확대) - A1: CLAUDE.md L33~49 C1~C31/P1~P28 체계 갱신 + 폐기 규칙 1줄 압축 - A2: SKILL.md 17+ 개소 5단계 chunk 분할 정리 (활성 운영 지침 P20 참조 0건 달성) - A3: 클라이언트·서버팀장 P20 → P24·C27·C29-4·C30·3축 감사 - A3 확장 (PM 재량): pm-general·개발팀장·기획팀장 frontmatter 갱신 - B1: 07 Headless 원안 아카이브 배너 + 02_점검 L19 주해 - B2: 02 추출대상 완료 실적 배너 + Tier 1 16/16 구현 경로 역참조 - B3: 08 전투시스템 SOT Q-P2 실측 수치 반영 (#37, PCDefence_Mul=0.3) - B4: 집행 불요 (실측 결과 이미 해결 상태) ## 검증 - verify_log_paths.sh 4건 실존 유지 - verify_references.sh 신규 파손 0건 - .live/ 7건 동기 기록 (P25 실증) - 대화로그 2026-04-18 엔트리 (기각안 필수 필드 포함) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:54:32 +00:00
#### ✅ 실측 확정 수치 (2026-04-17 #37 Q-P2 정밀 2차)
| 항목 | 기획 초기 가정 | **실측 확정값** | 근거 |
|------|---------------|---------------|------|
| 피해 감소 비율 | 50% | **30%** (`PCDefence_Mul = 0.3`) | `GlobalValue.json``{"s_ID": "PCDefence_Mul", "n_Value": "0.3"}` |
| 고정 절대 감소 | 미정 | **1** (`PCDefence = 1`) | `GlobalValue.json` + `Actor.cs:775` |
| 지속 형태 | 단일 공격 바인딩 가정 | **지속형 상태 효과** (터치 Down↔Up 유지 동안 반복 적용) | `UITouchHandler.cs` + `IngameUIManager.cs:39` |
| 쿨다운 | 미정 | **없음** (즉시 재사용) | `UITouchHandler.cs`·`PCActor.cs:148-162` 전수 확인 |
| 방어 중 공격 | 미정 | **불가** (DPS 0) | `Actor.cs:4500` — 공격 프레임 return |
| 방어 중 피격 애니 | 미정 | **차단** (Play_Hit 조기 return) | `PCActor.cs:133` |
| 적용 공격 종류 | 미정 | **Melee / Ranged 공통** | `Actor.cs:782-783` 단일 분기 |
| Mob 방어 메커닉 | 미정 | **부재** | `MobActor.cs` override 전수 확인 결과 부재 |
**근거 응답서**: [`2026-04-17_Phase0-C_QP2_정밀2차_응답서.md`](../../../공유/소통/완료/2026-04-17_Phase0-C_QP2_정밀2차_응답서.md) (#37 PD 지시). **기획 초기 가정("50%")은 추적성 보존을 위해 유지**(원칙 1), 실측 수치로 전환하여 밸런스 작업은 0.3 기준으로 진행.
---
## 5. 몬스터 배치 · 스폰 패턴 (Q-P2)
### 5.1 배치 흐름
`MonsterNodeControler.Set()` (`cs:42~171`)
1. `StageNodeData_Mob.PatternID``table_MonsterPatternList` 조회
2. `PatternID < 1001`이면 `+900` 구데이터 호환 (`cs:70`)
3. `Get_MakeMob_Dynamic()`가 라인 × 슬롯 9개에 대해 근접/원거리/고유 확률 누적 추첨
4. **고유 몬스터는 1개 제한** (`uniqueMakeCount`) — **(확인 필요)** 의도된 캡인지
### 5.2 데이터 테이블 키 매핑
| 테이블 | 필드 | 역할 | 위치 |
|---|---|---|---|
| `table_MonsterPatternList` | `n_PatternId` | 패턴 ID | `cs:11` |
| 동 | `list_MeleeRate_Front/Middle/Back` | 라인별 근접 확률 | `cs:33` |
| 동 | `list_RangeRate_Front/Middle/Back` | 라인별 원거리 확률 | `cs:34` |
| 동 | `list_FixedRate_Front/Middle/Back` | 라인별 고유 확률 | `cs:35` |
| 동 | `n_FixedMonsterId` | 고유 몬스터 ID | `cs:17` |
| `StageNodeData_Mob` | `MobCount` | 배치할 몬스터 수 | `IngameStageData.cs:86` |
| 동 | `list_MobID` | 선택 가능한 몬스터 풀 | `IngameStageData.cs:86` |
확률 문자열 포맷: `^`로 구분된 정수(10000 스케일) → 파싱 시 10000으로 나눠 0~1 정규화 (`table_MonsterPatternList.Init()` `cs:43~115`).
### 5.3 몬스터 ID 결정
`MonsterNodeControler.cs:183~196`
```csharp
int Get_MobID(MakeMobData mob, StageNodeData_Mob mobnode, int makecount, int bossid)
{
if (mob.MobID > 0) return mob.MobID; // 고유 몬스터 즉시 반환
var lst = mobnode.list_MobID;
if (bossid > 0) lst = lst.FindAll(f => f != bossid); // 보스 ID 중복 방지
if (mobnode.MakeRandom) return lst[Random.Range(0, lst.Count)];
return MyValue.m_MyStageData.Get_MobID_onStage(mob.e_ToolMobType, mobnode.list_MobID);
}
```
---
## 6. 보스 처리 (Q-P3)
### 6.1 판정
`table_monsterlist.cs:36~39`
```csharp
public override bool IsBoss()
=> e_MonsterType == eMonsterType.Boss_Melee
|| e_MonsterType == eMonsterType.Boss_Range;
```
### 6.2 배치 규칙
`MonsterNodeControler.cs:76~90`
```csharp
if (stagedata.m_Type == eStageNodeType.Boss)
{
++makeMobCount;
var bossdata = stagedata.Get_Data<StageNodeData_Boss>();
bossid = bossdata.BossID;
dic_mob[eMobBattlePos.Backline][2] = bossid; // 대기열 중앙 고정
...
}
```
**보스는 항상 Backline 슬롯 2에 배치된다.**
### 6.3 특수 패턴
- **Death 특수효과 피해**: 보스는 50% HP 피해, 일반 몬스터는 9999 (즉사) — `Actor.cs:570`
- **미션 등급 가중치**: `MobActor.cs:123~220` — 보스=2, 악마=3, 일반=1
> **(확인 필요)** 보스10002 클리어 가능성(Q-P3)은 전용 AI 패턴이 아닌 **스탯·카드 풀 기반 시뮬레이션**으로 검증해야 한다. 현재 코드상 보스 전용 공격 로직은 발견되지 않음.
---
## 7. 발견된 리스크 · 이슈
### 7.1 기획 문서 vs 코드 불일치 후보
| 항목 | 코드 실태 | 위치 |
|---|---|---|
| 회피 공식 | `HitRate / (Avoid × 3.69)` — 3.69 근거 불명 | `Actor.cs:623` |
| PC 방어 | 터치는 타겟팅만 확인, 명시적 방어 윈도우 미확인 | `PCActor.cs`, `MonsterNodeControler.cs` |
| 크리 상한 | 0.9f 고정 (90%) | `MyValue.cs:23` |
| 회피 상한 | PC만 0.9f, 몬스터는 정수형 | `MyValue.cs:625~639` |
### 7.2 하드코딩 값 (밸런싱 숨김)
| 값 | 용도 | 위치 | 개선 |
|---|---|---|---|
| `3.69` | 회피 상수 | `Actor.cs:623` | 테이블화 |
| `0.01f` | 회피율 % 변환 | `MyValue.cs:629` | eStat에서 처리 |
| `"PCDefence"` | 방어 절대 감소 | `Actor.cs:775` | GlobalValue 참조 OK |
| `"PCDefence_Mul"` | 방어 비율 감소 | `Actor.cs:783` | GlobalValue 참조 OK |
| `list_scale[]` | 9슬롯 스케일 | `MonsterNodeControler.cs:26` | 테이블화 |
| `list_pos[]` | 9슬롯 좌표 | `MonsterNodeControler.cs:27~32` | 테이블화 |
### 7.3 시뮬레이터 이원화
- 단일 경로 확인됨 — `Actor.Get_Dmg()` 외 병행 계산 경로 없음.
- 이원화 리스크는 **낮음**. (기획실 시뮬레이터는 외부 엑셀/도구이므로, SOT를 이 문서로 일원화하면 검증 기준 확정 가능.)
### 7.4 코드 품질
- `Actor.cs` ≈ 3,700줄 · `Get_Dmg` ≈ 300줄 — 분할 권고
- 카드 효과 조건분기 난립 (`eCardType.Max = 200+`) — 효과 테이블화·전략 패턴 리팩토링 후보
- `ObscuredInt/Float` 빈번 사용 — 보안 목적 유지하되 핫패스 프로파일링 필요
### 7.5 버그 냄새
1. **HP 0 허용 가능성**`Math.Max(1, ...)` 가드가 Attack_Min/Max에만 적용 (`MyValue.cs:709`)
2. **몬스터 회피 비일관성** — PC 상한 0.9 vs 몬스터 정수
3. **라인 변경 중 공격 수신**`Co_LineChange` 도중 상태 불일치 가능성
4. **보스 ID 풀 중복 처리 의존**`list_MobID`에 boss가 들어가 있을 때 `FindAll` 예외 안전성 **(확인 필요)**
---
## 8. B-1 완료 조건 체크리스트
SOT를 완전 확정하려면 다음 항목이 추가로 필요.
- [ ] 기획 엑셀 SOT와 회피·크리 상한·3.69 상수 대조
- [ ] `table_GlobalValue` 실제 값 덤프 (PCDefence, PCDefence_Mul, PC_ProjectileSpeed 등)
- [ ] `eCardType.G1_* ~ G5_*` 200+개 효과 전수 맵 (효과 종류별 분류표)
- [ ] 상태이상 중첩 규칙 명문화 (Stun/Freezing 등 CC 상호작용)
refactor(rules): 원칙 3 개정 + A+B급 6건 조직 룰 최적화 집행 ## PD님 승인 범위 - 2026-04-18 원칙 3 개정 (저장 위치 최적화 반영) - 2026-04-17 저녁 "옵션 나" A+B급 7건 일괄 승인 ## 수정 3대 원칙 (개정 반영) 1. 자산 보존 분리 — 배너·아카이브 섹션·외부 파일 2. sed 일괄 치환 금지 — 수동 정밀 치환 3. 폐기 선언 자산 유지 + 저장 위치 최적화 (신) — 활성 본문 1줄 + 외부 아카이브 파일 ## Phase 1 원칙 3 개정 - 공유/조직공지/폐기_규칙_아카이브.md 신설 (P20·C17·구 C18·구 C24·구 C26 5종 이관) - 인계서 §1 원칙 3 전면 개정 ## Phase 2 A+B급 집행 (6건 + PM 재량 확대) - A1: CLAUDE.md L33~49 C1~C31/P1~P28 체계 갱신 + 폐기 규칙 1줄 압축 - A2: SKILL.md 17+ 개소 5단계 chunk 분할 정리 (활성 운영 지침 P20 참조 0건 달성) - A3: 클라이언트·서버팀장 P20 → P24·C27·C29-4·C30·3축 감사 - A3 확장 (PM 재량): pm-general·개발팀장·기획팀장 frontmatter 갱신 - B1: 07 Headless 원안 아카이브 배너 + 02_점검 L19 주해 - B2: 02 추출대상 완료 실적 배너 + Tier 1 16/16 구현 경로 역참조 - B3: 08 전투시스템 SOT Q-P2 실측 수치 반영 (#37, PCDefence_Mul=0.3) - B4: 집행 불요 (실측 결과 이미 해결 상태) ## 검증 - verify_log_paths.sh 4건 실존 유지 - verify_references.sh 신규 파손 0건 - .live/ 7건 동기 기록 (P25 실증) - 대화로그 2026-04-18 엔트리 (기각안 필수 필드 포함) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:54:32 +00:00
- [x] PC 방어 입력 경로 특정 (UI 버튼 vs 터치 윈도우) — **완료 (2026-04-17 #37)**: `UITouchHandler` 기반 터치 윈도우, `IngameUIManager.cs:39` 바인딩
- [x] Q-P1 응답서 확정 — 터치 방어 구조 최종 확인 — **완료 (2026-04-17 #37 Q-P2 정밀 2차)**: [응답서](../../../공유/소통/완료/2026-04-17_Phase0-C_QP2_정밀2차_응답서.md) 참조
- [ ] Q-P2 응답서 확정 — 패턴별 배치 예시 생성 (본 항목은 몬스터 배치 패턴 예시 별개 작업)
- [ ] Q-P3 응답서 확정 — 보스10002 시뮬 결과 포함
- [ ] 핫패스 성능 프로파일 (Get_Dmg 호출/프레임, ObscuredInt 오버헤드)
- [ ] 위 7.5 버그 냄새 재현 테스트
---
## 9. 변경 이력
| 버전 | 일자 | 작성자 | 내용 |
|---|---|---|---|
| v1 | 2026-04-14 | 개발실장 (Explore 위임) | 초안 — Phase 0-B-1 |