BurningTimesAi/공유/기획/GodDem/2026-08-23_B4ele_원작정합_재설계_v...

371 lines
34 KiB
Markdown
Raw Permalink Normal View History

# GodDem ele 2종(Node2·3) 원작 정합 재설계 v2 — 영웅 등급 차원 구조 피벗
> **작성**: balance-designer(기획팀) 2026-08-23 · **P3-B4 후속 보정 산출물**(v1 병존, GodDem 레포 수정 0건)
> ⚠️ **v1은 역사 보존 — 최신 SOT 아님**: `2026-08-23_B4ele_원작정합_재설계_v1.md`(대표 계열 1개 고정 채택안, plan-auditor 조건부통과·보강 5건 완료)는 PD가 그 전제 자체를 폐기하며 대체됐다. v1의 원작 실측치(계열93/94/95·73/74/75)·D(grade) 비용 방법론·per-node MaxGrade 인프라(`102e662`)·층간 교차표 원칙은 **전부 유효 승계**, 대표 계열 "택1" 구조만 폐기.
> **PD 원문 (2026-08-23, 대화로그 §98, C42-2 A)**: "지금은 초기 버전이라 영웅이 1명이지만 **추후에는 영웅을 늘릴 예정**이므로 **원작과 동일하게 영웅 등급 별로 맞춰**"
> **PD 의도 분석(C42-2 B)**: v1의 4안 비교("어느 계열을 대표로 고정할까")는 질문 자체가 잘못됐다 — PD는 택일이 아니라 **로드맵 정보 공개 + 구조 지시**를 한 것이다. "영웅을 늘릴 예정"이라는 사실이 "GodDem은 히어로 1명이라 대표값이 필요하다"는 v1의 핵심 전제(§1-2)를 무효화한다. 원작이 실제로 가진 "영웅 등급별 계열 게이팅" 데이터 구조를 GodDem도 그대로 보유해야, 다음 히어로 추가 시 재설계 없이 그 히어로의 등급만 지정하면 된다.
> **표기 규칙(C5·C44)**: 🟢확정(원작 실측 또는 코드 직접 확인) · 🟡추정 · 🔴PD 확인 필요
---
## 0. 결론 요약
| 결정 항목 | 채택 | 핵심 근거 |
|---|---|---|
| **데이터 구조** | CSV에 **`n_HeroGrade`(4/5/6) 컬럼 신설** — 원작 3개 계열(93·94·95 / 73·74·75) 전체가 설계 근거상 24행(원작 발자국)이나, **CSV 실수록은 16행**(HeroGrade5 8행은 재추출 전까지 미수록, §1-2 M-2·m-1 정정). 테이블 3차원화(NodeId×HeroGrade×MasteryGrade) | 원작 `heroskillattr`가 실제로 갖는 (계열×quality) 2차원 구조를 그대로 보존 — 다음 히어로 추가 시 테이블 재작업 0건. 미확정 추정치는 데이터 파일에 박지 않는다(C5) |
| **현 히어로 등급 배정** | **HeroGrade=4(최저 등급) 고정 배정** — 신규 `SurvivalMetaData.HeroGrade` 필드, 기본값 4 | (a) B1 승급(star) 매핑은 **원작 실측으로 기각**(아래 §2) — (b) 신규 필드 고정 배정 채택. "시작 히어로 = 최저 등급"은 F2P 장르 관행 + 향후 히어로 확장의 최대 헤드룸 확보 |
| **Node1(penetrate_ratio) 편입** | **보류(스키마는 호환, 값 변경은 Q2 답변 후)** | PD Q2("관통 적용 방식·계열 상향 의미" 설명 요청)가 별도 트랙으로 진행 중 — 본 문서가 선제 결정하면 C36(PD 결정 영역 선점) 소지 |
| **경제·승산항(현 활성 HeroGrade=4 기준)** | 3노드 합계 **126,390G**(v1 §6 "93/73" 행과 동일치 재사용) · 승산항 0.30 · 결합 상한 **1.60**(구 1.72 대비 -7.0%) | HeroGrade5·6은 설계 근거상 테이블에 존재(6은 CSV 수록·5는 재추출 대기)하나 현재 비활성(다음 히어로 또는 향후 등급승급 메커니즘 도입 전까지 미접근) |
| **★ N-1 전제 재이동(v1과 반대 방향, PD 인지 필요)** | 가챠/비가챠예산 배율 **1.7배→2.08배로 악화**(v1의 95/75안은 1.23배로 완화 예상이었으나, 원작 정합 재검증 결과 반대 결론) | §5-2 |
---
## 1. 데이터 구조 재설계
### 1-1. 원작 구조 재확인 — 왜 "대표 계열 고정"이 틀렸는가(C39, 재검증)
v1은 개발팀장 §87 실측(계열93/94/95·73/74/75, 3개 병렬 계열)을 "어차피 GodDem은 히어로 1명이니 그중 하나만 고르면 된다"고 처리했다. 이번 피벗을 계기로 원작의 인접 시스템(`hero_star.csv`, B1이 이미 이식한 승급 체계)을 **직접 재대조**한 결과, 이 처리가 구조적으로 틀렸다는 강한 증거를 찾았다(C44 — PM 위임 시점에 없던 신규 근거):
> **B1 v1 §4-1 원문 인용(본 세션이 작성한 기존 문서, 2026-08-22)**: "원작 `hero_star.csv`(186행=6등급×31성)의 진짜 기능은 % 보너스가 아니라 레벨 캡 게이팅... **GodDem은 히어로가 1명뿐이라 원작의 "quality"(진영별 희귀도 1~6) 축이 성립하지 않는다**"
즉 원작은 **모든 시스템에서 일관되게 "히어로 등급/quality = 어느 히어로(진영)를 보유했는가에 따라 고정되는 축", "star/학습레벨 = 그 히어로에 투자해서 늘리는 축"**이라는 두 축을 분리해 왔다(`hero_star`의 quality vs star, `heroskillattr`의 계열 vs quality-내부-학습레벨). 🟡 이 패턴이 `heroskillattr`의 ele 슬라이스(93/94/95·73/74/75)에도 동일하게 적용된다고 보는 것이 **가장 근거 있는 해석**이나, `heroskillattr` 자체에서 "계열=고정 식별자"라고 직접 명시한 원문 문구를 확보한 것은 아니다(추론, 미확정 태그) — "계열"(hero grade)은 **어느 히어로를 보유했는지에 따라 고정**되지, 한 히어로가 투자로 "성장해서" 올라가는 축이 아니다.
**결론**: v1처럼 "대표 계열 1개만 남기고 나머지 버리기"는 원작이 실제로 갖고 있던 **히어로별 고정 계열**이라는 구조 자체를 소거하는 것이었다 — PD가 "영웅 등급별로 맞춰"라고 한 것은 정확히 이 구조를 되살리라는 지시다.
### 1-2. CSV 스키마 재설계
**컬럼 추가**: `n_NodeId,s_StatKey,n_HeroGrade,n_Grade,f_Value,l_Cost` (기존 대비 `n_HeroGrade` 1열 삽입, `n_Grade`는 기존과 동일하게 **마스터리 강화 단계**를 의미 — 이름 충돌 방지를 위해 이하 "HeroGrade"(영웅 등급)와 "MasteryGrade"(강화 단계)로 구분 표기).
**"등급 컬럼 추가" vs "계열별 파일 분리" 비교**: 후자(예: `SurvivalMetaSkillMastery_Grade4.csv`/`_Grade5.csv`/`_Grade6.csv` 3파일 분리)는 기각 — 로더가 3배로 늘고(`Load()` 3회 호출·경로 3종 관리), 향후 히어로 5번째 등급이 추가되면 파일 자체를 새로 만들어야 한다. **컬럼 추가안은 로더 1개·파일 1개 유지, 행만 늘어난다**(3중 SOT 방지 원칙 계승).
**원작 전체 데이터 발자국은 24행**(v1의 12행에서 2배, Node1 3노드 중 Node2·3만 해당)이나, **CSV 실수록분은 16행**이다 — HeroGrade5(계열94/74) 8행은 아래 M-2·m-1 정정에 따라 재추출 전까지 CSV에서 제외한다:
| NodeId | HeroGrade | 원작 계열(참고) | MasteryGrade 범위 | 행 수 | CSV 수록 |
|---|---|---|---|---|---|
| 2(ele_hurt_add) | 4 | 계열93 | 1~3 | 3 | 🟢 수록 |
| 2 | 5 | 계열94 | 1~4 | 4 | 🔴 **미수록**(§1-2 M-2·m-1 정정) |
| 2 | 6 | 계열95 | 1~5 | 5 | 🟢 수록 |
| 3(ele_penetrate_ratio) | 4 | 계열73 | 1~3 | 3 | 🟢 수록 |
| 3 | 5 | 계열74 | 1~4 | 4 | 🔴 **미수록**(§1-2 M-2·m-1 정정) |
| 3 | 6 | 계열75 | 1~5 | 5 | 🟢 수록 |
**★ M-2·m-1 정정(plan-auditor 반영) — HeroGrade5(계열94/74) 8행은 재추출 전까지 CSV에서 제외**: §4-1·4-2가 보이듯 계열94/74의 개별 스텝값은 §87 실측 원문이 범위("0.06~0.24"·"0.04~0.16")만 제공했을 뿐 개별 스텝 4개를 확정하지 않아 **선형 외삽(🟡 추정)**이다. 다음 두 처리안 중 **(가) CSV 미수록**을 채택한다:
- **(가) 채택 — 재추출 전까지 CSV에서 완전 제외**(4·6등급 16행만 수록): 추정치가 실제 데이터 파일에 박히면 이후 이 CSV만 보는 사람(개발팀장·차기 세션)이 실측으로 오인할 위험이 있다 — "원작 실측만 CSV에 수록한다"는 원칙(Node1이 이미 §5-1에서 지켜온 관행과 동일)을 지킨다. 현재 HeroGrade4만 활성(§2)이라 게임 동작에도 영향이 없다.
- (나) 기각 — 🟡 표기 행으로 CSV에 유지: `l_Cost`/`f_Value` 컬럼에는 애초에 확정/추정 태그를 실을 여지가 없어(순수 수치 컬럼) CSV 자체로는 "이 행이 추정"이라는 사실을 전달할 방법이 없다 — 결국 (가)와 달리 오인 위험을 CSV 파일 스스로 해소하지 못한다.
**교차검증(C44)**: B4 v1 §4-1이 인용한 매핑v1 원본 엔트리 수("ele_hurt_add 12"·"ele_penetrate_ratio 12")와 3+4+5=12가 정확히 일치 — **원작 전체 24행 구조**가 `heroskillattr`의 ele 슬라이스 전체(88엔트리 중 12+12=24개)를 빠짐없이 담고 있음을 재확인했다(이 24행은 설계 근거로서 유효, CSV 실수록 16행과는 별개 개념).
### 1-3. 로더·소비 코드 파급 명세(개발팀장 구현 스펙 — plan-auditor 정정 5건 반영, 실제 GodDem 소스 재대조 완료)
> **정정 고지(C23 자진 정정)**: 아래는 v2 최초본의 §1-3 전사 오류를 실제 코드(`SurvivalSkillMasteryTable.cs`·`SurvivalMeta.cs:484-522`·`SurvivalLobbyController.SkillMastery.cs:142-158`, 2026-08-23 재실측)와 다시 대조해 **전면 재작성**한 것이다. 최초본은 "설계는 정확하나 코드 조각 전사에서 5건의 실수"(plan-auditor 평가)가 있었다 — 로더 인덱스가 실제 CSV 순서와 어긋나 `int.Parse`가 문자열(`s_StatKey`)을 파싱해 즉시 `FormatException`을 던지는 등, 그대로 구현하면 마스터리 시스템이 전면 정지했을 것이다.
**`SurvivalSkillMasteryTable.cs`**(v1 §3의 per-node MaxGrade 인프라〔`102e662` 위에 **추가 확장** — 재작성 아님. 아래 실제 현재 코드 기준 diff):
```diff
public class Row
{
public int NodeId;
+ public int HeroGrade; // 신규 — 영웅 등급(4/5/6). Node1 등 미편입 노드는 단일값 고정 사용
public int Grade; // 기존 의미 그대로: 마스터리 강화 단계
public float Value;
public long StepCost;
}
- readonly Dictionary<(int nodeId, int grade), Row> _rows = new();
+ readonly Dictionary<(int nodeId, int heroGrade, int grade), Row> _rows = new();
- readonly Dictionary<int, int> _maxGradeByNode = new();
+ readonly Dictionary<(int nodeId, int heroGrade), int> _maxGradeByNode = new();
- public int MaxGradeOf(int nodeId) => _maxGradeByNode.TryGetValue(nodeId, out int g) ? g : 0;
+ public int MaxGradeOf(int nodeId, int heroGrade) =>
+ _maxGradeByNode.TryGetValue((nodeId, heroGrade), out int g) ? g : 0;
```
**로더(C-1 정정)** — 신 CSV 헤더 `n_NodeId,s_StatKey,n_HeroGrade,n_Grade,f_Value,l_Cost`(§1-2)의 **실제 순서 그대로**(최초본은 c[1]에 문자열 컬럼 `s_StatKey`를 건너뛴다고 주석에 적어놓고 실제로는 `HeroGrade = int.Parse(c[1])`로 그 문자열을 파싱해버리는 자기모순이 있었다 — 아래는 실제 인덱스로 재작성):
```diff
- if (c.Length < 5) continue;
+ if (c.Length < 6) continue; // 컬럼 1개 추가(n_HeroGrade)
var row = new Row
{
NodeId = int.Parse(c[0]),
// c[1] = s_StatKey — 스킵(기존과 동일 관례, 변경 없음)
+ HeroGrade = int.Parse(c[2]), // 신규 컬럼(CSV 3번째 필드)
- Grade = int.Parse(c[2]),
+ Grade = int.Parse(c[3]), // 기존 c[2]→c[3]
- Value = float.Parse(c[3], inv),
+ Value = float.Parse(c[4], inv), // 기존 c[3]→c[4]
- StepCost = long.Parse(c[4]),
+ StepCost = long.Parse(c[5]), // 기존 c[4]→c[5]
};
- table._rows[(row.NodeId, row.Grade)] = row;
+ table._rows[(row.NodeId, row.HeroGrade, row.Grade)] = row;
- if (!table._maxGradeByNode.TryGetValue(row.NodeId, out int cur) || row.Grade > cur)
- table._maxGradeByNode[row.NodeId] = row.Grade;
+ var key = (row.NodeId, row.HeroGrade);
+ if (!table._maxGradeByNode.TryGetValue(key, out int cur) || row.Grade > cur)
+ table._maxGradeByNode[key] = row.Grade;
```
**`ValueAt`**:
```diff
- public float ValueAt(int nodeId, int grade)
+ public float ValueAt(int nodeId, int heroGrade, int grade)
{
if (grade <= 0) return 0f;
- if (grade > MaxGradeOf(nodeId)) grade = MaxGradeOf(nodeId);
+ if (grade > MaxGradeOf(nodeId, heroGrade)) grade = MaxGradeOf(nodeId, heroGrade);
- return _rows.TryGetValue((nodeId, grade), out var r) ? r.Value : 0f;
+ return _rows.TryGetValue((nodeId, heroGrade, grade), out var r) ? r.Value : 0f;
}
```
**`CumulativeCostAt`**(M-1 정정 — "동일 패턴" 뭉개지 않고 루프 내부까지 전부 명시):
```diff
- public long CumulativeCostAt(int nodeId, int grade)
+ public long CumulativeCostAt(int nodeId, int heroGrade, int grade)
{
if (grade < 0) grade = 0;
- if (grade > MaxGradeOf(nodeId)) grade = MaxGradeOf(nodeId);
+ if (grade > MaxGradeOf(nodeId, heroGrade)) grade = MaxGradeOf(nodeId, heroGrade);
long sum = 0L;
for (int g = 1; g <= grade; g++)
- if (_rows.TryGetValue((nodeId, g), out var r)) sum += r.StepCost;
+ if (_rows.TryGetValue((nodeId, heroGrade, g), out var r)) sum += r.StepCost;
return sum;
}
```
**Node1(penetrate_ratio) 호환성**: 아직 단일 계열(10)만 쓰므로 CSV에 `n_HeroGrade` 컬럼을 추가하되 **6행 전부 같은 값(예: 편의상 4)을 채워** 스키마만 맞춘다 — Q2 답변 후 Node1도 다중 계열로 전환되면 이 자리에 실제 등급별 행을 추가하기만 하면 된다(현재는 값 변경 없음, 스키마 호환만).
**`SurvivalMeta.cs`**(C-2 정정 — 실제로는 신규 필드 1개 + **메서드 4개**〔최초본은 3개만 나열〕 내부 로직 수정, 시그니처는 기존과 동일 유지. 실제 소스 라인 484-522 재대조):
```diff
// SurvivalMetaData
+ /// <summary>현재 히어로의 고정 등급(4/5/6, 원작 heroskillattr 계열 대응). 다영웅 확장 시
+ /// 히어로별로 이 필드가 각각 다른 고정값을 가지게 된다(원작 hero_star.csv quality축과 동일 성격).
+ /// 본 필드는 성장/승급으로 변하지 않는다 — B1 PromotionStar와는 독립인 축(§2 근거).</summary>
+ public int HeroGrade = 4;
```
```diff
// ★ C-2 정정 — 최초본이 누락했던 4번째 메서드(MasteryAttackRatio, L494-497 실측)
public static float MasteryAttackRatio() =>
- SkillMastery.ValueAt(1, MasteryGradeOf(1)) // mastery_penetrate_ratio
- + SkillMastery.ValueAt(2, MasteryGradeOf(2)) // mastery_ele_hurt_add
- + SkillMastery.ValueAt(3, MasteryGradeOf(3)); // mastery_ele_penetrate_ratio
+ SkillMastery.ValueAt(1, Data.HeroGrade, MasteryGradeOf(1)) // mastery_penetrate_ratio
+ + SkillMastery.ValueAt(2, Data.HeroGrade, MasteryGradeOf(2)) // mastery_ele_hurt_add
+ + SkillMastery.ValueAt(3, Data.HeroGrade, MasteryGradeOf(3)); // mastery_ele_penetrate_ratio
public static int MasteryMaxGradeOf(int nodeId) =>
- SkillMastery.MaxGradeOf(nodeId);
+ SkillMastery.MaxGradeOf(nodeId, Data.HeroGrade);
public static bool CanUpgradeMastery(int nodeId) =>
- MasteryGradeOf(nodeId) < SkillMastery.MaxGradeOf(nodeId);
+ MasteryGradeOf(nodeId) < SkillMastery.MaxGradeOf(nodeId, Data.HeroGrade);
public static long MasteryUpgradeCost(int nodeId)
{
int cur = MasteryGradeOf(nodeId);
- if (cur >= SkillMastery.MaxGradeOf(nodeId)) return -1;
- return SkillMastery.CumulativeCostAt(nodeId, cur + 1) - SkillMastery.CumulativeCostAt(nodeId, cur);
+ if (cur >= SkillMastery.MaxGradeOf(nodeId, Data.HeroGrade)) return -1;
+ return SkillMastery.CumulativeCostAt(nodeId, Data.HeroGrade, cur + 1)
+ - SkillMastery.CumulativeCostAt(nodeId, Data.HeroGrade, cur);
}
```
`GachaAttackRatio()`류처럼 `Data.*`를 내부에서 읽는 기존 관행 그대로 — **`HeroGrade`는 암묵적 컨텍스트로 흡수**된다.
**`SurvivalLobbyController.SkillMastery.cs`**(C-3 정정 — "UI 레이어 변경 0" 주장은 실측으로 반증됨. `RefreshMasteryPanel()` L156이 `SurvivalMeta`의 래퍼 메서드를 거치지 않고 `SurvivalMeta.SkillMastery.ValueAt(nodeId, g)`를 **직통 호출**하는 지점이 실제로 존재했다):
```diff
int max = SurvivalMeta.MasteryMaxGradeOf(nodeId); // 시그니처 불변 — 무변경
int g = SurvivalMeta.MasteryGradeOf(nodeId); // 시그니처 불변 — 무변경
- float val = SurvivalMeta.SkillMastery.ValueAt(nodeId, g);
+ float val = SurvivalMeta.SkillMastery.ValueAt(nodeId, SurvivalMeta.Data.HeroGrade, g);
```
**정정된 실제 파급 범위**: "UI 레이어 변경 0건"이 아니라 **"`SurvivalMeta`의 3개 래퍼 메서드(`MasteryMaxGradeOf`·`CanUpgradeMastery`·`MasteryUpgradeCost`)는 시그니처 불변, `ValueAt` 직통 호출 1줄만 수정"**이다 — `SurvivalMeta.Data``public static SurvivalMetaData Data`(L122 실측)로 이미 공개 프로<ED9484>터티라 UI에서 직접 참조 가능.
**마이그레이션**: `SurvivalMetaData.Version`(현재 5) → **6**. `Load()``if (_data.HeroGrade <= 0) _data.HeroGrade = 4;` 1줄 추가(기존 세이브 파일 호환, 신규 필드 기본값 보장).
### 1-4. 시그니처 변경 시 전 호출부 재확인 원칙(plan-auditor 권고 수용, 신규 — 차기 계승)
본 정정 5건은 전부 "시그니처를 바꾸는 diff를 작성하면서 그 심볼의 기존 호출부를 전수 grep하지 않은" 동일 원인에서 비롯됐다(`ValueAt`·`CumulativeCostAt`·`MaxGradeOf`가 몇 곳에서 불리는지 재확인 없이 "설계상 이래야 한다"만으로 작성). **원칙화**: 이후 balance-designer가 기존 메서드의 시그니처를 바꾸는 diff를 작성할 때는, 반드시 그 심볼명으로 GodDem 전체를 grep해 호출부 전수를 확인한 뒤 각 호출부의 diff를 명시한다 — 층간 교차표 필수화(v1 §6-1 원칙) 옆에 나란히 두는 상시 체크리스트 항목으로 삼는다.
---
## 2. ★ 현 히어로 등급 배정 — 핵심 결정
### 2-1. 후보 평가
| 안 | 내용 | 원작 구조 정합 | 다영웅 확장 재작업 |
|---|---|---|---|
| **(a) B1 승급(star) 매핑** | star 구간별로 HeroGrade 4→5→6 승격(예: star0~3=4·4~7=5·8~11=6) | **기각** — §1-1 실측: 원작에서 quality(등급)와 star는 **서로 다른 원작 파일의 직교 축**(`hero_star.csv` 자체가 quality×star 2차원이며 quality="진영별 희귀도"라고 B1 v1이 명시). star를 grade의 대용으로 쓰면 원작에 없는 인위적 결합을 만드는 것 — "원작과 동일하게 맞춰"라는 PD 지시와 정면 배치 | 신규 히어로마다 "이 히어로는 star 몇부터 시작하는가"라는 원작에 없는 규칙을 새로 발명해야 함 — 확장할수록 재작업 증가 |
| **(b) 히어로 등급 필드 신설 + 고정 배정 — 채택** | `SurvivalMetaData.HeroGrade` 신규(기본 4), 승급과 완전 독립 | **정합**`hero_star.csv`의 quality축과 동일한 "히어로 정체성에 고정된 값" 패턴 그대로 재현 | 신규 히어로 추가 = 그 히어로의 `HeroGrade` 값 지정 1줄. 테이블 데이터는 이미 4/5/6 전부 있어 **재작업 0건** |
| (c) 기타(HeroLevel 구간 매핑 등) | HeroLevel(0~60) 구간별 등급 승격 | 기각 — HeroLevel도 원작의 별도 파일(`hero_level.csv`, B1 §1-2가 이미 이식)이 원본이라 (a)와 동일한 "서로 다른 원작 축 강제 결합" 오류 반복 | (a)와 동일한 확장 부담 |
### 2-2. 현 히어로 등급 값 — HeroGrade=4(최저) 배정 근거
1. **원작 데이터 가용 범위**: ele 2종은 원작에서 애초에 등급4 미만(1~3)에는 데이터 자체가 없다(§87 실측) — 4가 ele 접근의 최저 문턱이며, 현재 GodDem 히어로가 이미 Node2·3에 접근 가능한 상태(B4 설계 전제)와 정합하는 **가장 낮은 유효값**이다.
2. **F2P 장르 관행**: 최초 플레이어블 캐릭터를 최고 희귀도로 배정하는 상용 서비스는 드물다 — "시작 캐릭터=최저~중간 등급, 이후 뽑기/이벤트로 상위 등급 획득"이 표준 온보딩 곡선이며, GodDem 자체도 B3(가챠)에서 이미 이 원칙(신규유저는 낮은 확률로만 고등급 도달)을 채택 중이다.
3. **다영웅 확장 헤드룸 최대화**: 현재 히어로를 4로 시작하면, 이후 5·6등급 히어로를 "상위호환 신규 캐릭터"로 자연스럽게 배치할 수 있다 — 반대로 지금 6(최고)으로 배정하면 향후 히어로는 전부 "동급 이하"가 되어 신규 캐릭터의 성장 서사(더 강한 캐릭터를 얻는 재미)를 설계할 여지가 사라진다.
4. **Node1과의 정합**: Node1(penetrate_ratio, 계열10)도 "7개 동등 계열 중 최저"를 이미 선택해 둔 전례(B4 v1 §5-1)가 있다 — 최저값 우선이라는 이 세션의 일관된 재량 기준과 부합한다.
### 2-3. 기각안(C32)
| # | 검토안 | 기각 사유 |
|---|---|---|
| 1 | HeroGrade=6(v1의 최종 채택안 계승, 최고등급) | §2-2 — 다영웅 확장 헤드룸을 스스로 없애는 선택. v1의 "완전 맥스 표현"이라는 근거는 "히어로 1명이 영원히 유일하다"는 전제 위에서만 성립했는데 그 전제 자체가 PD 지시로 폐기됨 |
| 2 | HeroGrade=5(중간값) | 4·6 사이 절충 외에 별도 근거 없음 — 원작에서 5가 "표준/기본" 등급이라는 근거를 원작 데이터 어디에서도 찾지 못함(v1 §1-5의 94/74 기각 사유와 동일 성격 재확인) |
| 3 | (a) B1 승급 매핑 | §2-1 표 — 원작의 두 직교 축(quality 대 star)을 인위적으로 결합하는 구조적 오류 |
---
## 3. Node1(penetrate_ratio) 편입 — 보류(스키마 호환만)
PD Q2("관통이 어떻게 적용되고 있고, 최저→최고 계열이 무슨 의미인지 설명해")는 **아직 답변만 발신된 상태**(PM, 대화로그 §98)이며 결정은 그 이후다. 본 문서는 Q2의 답을 선점하지 않는다(C36) — 다만 §1-2에서 이미 밝혔듯 CSV 스키마(`n_HeroGrade` 컬럼)는 Node1도 즉시 수용 가능한 형태로 설계했다.
**개발팀장 재추출 필요 항목(PM 상신 요청)**: penetrate_ratio의 원작 7개 계열(10~16)이 ele처럼 "영웅 등급별 계열"인지, 아니면 다른 축(예: 순수 품질 티어)인지 미확인 상태다 — Node1이 이번 구조에 편입될 경우 이 대응 관계의 실측이 선행돼야 한다. 현재 이 정보 없이 Node1을 무리하게 편입시키면 v1이 저질렀던 것과 같은 종류의 미검증 가정(그때는 "형제 스탯 패턴 유추", 이번엔 "7계열=등급"이라는 미확인 가정)을 반복하게 된다 — **PM 경유 개발팀장 재추출 요청을 상신 권고**.
---
## 4. 수치 테이블 — 3개 HeroGrade 전체 (설계 근거 24행·CSV 실수록 16행, v1 §4-2·4-3 산출치 재구성)
**방법론(v1 §4-1 D(grade) 그대로 승계 — 재계산 불요, 이미 산출된 값 재구성만)**: `StepCost(MasteryGrade) = D(MasteryGrade) × 고정Δ`, D(1~6)=550/2,310/5,445/10,120/16,555/24,970 무변경.
### 4-1. Node2(mastery_ele_hurt_add) — 3개 HeroGrade
| HeroGrade | MasteryGrade | f_Value | StepCost | 누적 |
|---|---|---|---|---|
| **4(현재 활성, CSV 수록)** | 1 | 0.05 | 550×5=2,750 | 2,750 |
| 4 | 2 | 0.10 | 2,310×5=11,550 | 14,300 |
| 4 | 3 | 0.15 | 5,445×5=27,225 | **41,525** |
| 5(예비, 🔴**CSV 미수록** — 참고치) | 1 | 0.06🟡 | 550×6=3,300 | 3,300 |
| 5 | 2 | 0.12🟡 | 2,310×6=13,860 | 17,160 |
| 5 | 3 | 0.18🟡 | 5,445×6=32,670 | 49,830 |
| 5 | 4 | 0.24🟡 | 10,120×6=60,720 | **110,550** |
| 6(예비, CSV 수록) | 1 | 0.07 | 550×7=3,850 | 3,850 |
| 6 | 2 | 0.14 | 2,310×7=16,170 | 20,020 |
| 6 | 3 | 0.21 | 5,445×7=38,115 | 58,135 |
| 6 | 4 | 0.28 | 10,120×7=70,840 | 128,975 |
| 6 | 5 | 0.35 | 16,555×7=115,885 | **244,860** |
🟡 **HeroGrade5 값 표기 유의(§1-2 M-2·m-1 정정 반영)**: 원작 계열94는 §87 실측 원문이 "0.06~0.24" 범위만 제공했고 개별 스텝 4개 전부를 명시하지 않았다 — 위 표는 계열93·95가 공통으로 보이는 "엄격 선형(base×level)" 패턴을 계열94에도 적용한 **추정치**다(🟡, v1 §1-1과 동일 근거). **이 4행은 CSV에는 수록하지 않는다**(§1-2) — 본 표는 설계 근거·향후 재추출 대조용 참고치로만 보존한다. 계열4(HeroGrade5)는 현재 비활성이라 실제 게임에 영향이 없다 — 활성화 전 개발팀장 재추출로 🟢 격상 권고.
### 4-2. Node3(mastery_ele_penetrate_ratio) — 3개 HeroGrade
| HeroGrade | MasteryGrade | f_Value | StepCost | 누적 |
|---|---|---|---|---|
| **4(현재 활성, CSV 수록)** | 1 | 0.03 | 550×3=1,650 | 1,650 |
| 4 | 2 | 0.06 | 2,310×3=6,930 | 8,580 |
| 4 | 3 | 0.09 | 5,445×3=16,335 | **24,915** |
| 5(예비, 🔴**CSV 미수록** — 참고치) | 1 | 0.04🟡 | 550×4=2,200 | 2,200 |
| 5 | 2 | 0.08🟡 | 2,310×4=9,240 | 11,440 |
| 5 | 3 | 0.12🟡 | 5,445×4=21,780 | 33,220 |
| 5 | 4 | 0.16🟡 | 10,120×4=40,480 | **73,700** |
| 6(예비, CSV 수록) | 1 | 0.05 | 550×5=2,750 | 2,750 |
| 6 | 2 | 0.10 | 2,310×5=11,550 | 14,300 |
| 6 | 3 | 0.15 | 5,445×5=27,225 | 41,525 |
| 6 | 4 | 0.20 | 10,120×5=50,600 | 92,125 |
| 6 | 5 | 0.25 | 16,555×5=82,775 | **174,900** |
🟡 HeroGrade5(계열74, "0.04~0.16") 동일 유의사항 — **CSV 미수록**(§1-2 M-2·m-1 정정), 참고치로만 보존.
### 4-3. 밸런싱 제안 표준 포맷(현재 활성 HeroGrade4 기준)
| 항목 | 현재 값(v1 실질 반영분) | 제안 값(v2) | 근거 |
|---|---|---|---|
| CSV 스키마 | `n_NodeId,s_StatKey,n_Grade,f_Value,l_Cost`(단일 계열) | **`n_NodeId,s_StatKey,n_HeroGrade,n_Grade,f_Value,l_Cost`**(HeroGrade4·6 16행 수록, HeroGrade5 8행은 재추출 전까지 보류) | §1-2 — 원작 구조 보존, PD 지시 |
| Node2 활성 최댓값/총액 | (v1 5단 계획) 0.35 / 244,860G | **0.15 / 41,525G**(HeroGrade4 활성 슬라이스) | §2 — 현 히어로 등급4 배정 |
| Node3 활성 최댓값/총액 | (v1 5단 계획) 0.25 / 174,900G | **0.09 / 24,915G** | §2 |
| 3노드 합계(HeroGrade4 활성) | v1 479,710G(HeroGrade6 가정) | **126,390G**(Node1 59,950+Node2 41,525+Node3 24,915) | §2 — B2(510,496G) 대비 24.8%, 구B4(526,680G) 대비 -76.0% |
**세그먼트 영향**: v1과 동일 판정 — 전 세그먼트 동일(IAP 미연동).
---
## 5. 층간 결합 최종 수치 — "현재 도달 가능한 최고 등급" 기준 재산출
### 5-1. 승산항 상한(HeroGrade4 활성 기준)
| 구성 | 값 |
|---|---|
| 승급(B1) | 0~0.22 |
| 마스터리(HeroGrade4 활성, Node1+2+3) | 0~0.30(=0.06+0.15+0.09) |
| 가챠옵션(B3, N-1 확정) | 0~1.08 |
| **결합 상한** | **1+0.22+0.30+1.08 = 1.60**(구 1.72 대비 **-7.0%**) |
### 5-2. ★★ N-1 확정의 전제 재이동(v1과 반대 방향, PD 인지 필요·최우선 고지)
**정직 고지(C5·C44 — 이전 회차 보고와의 방향 불일치를 숨기지 않음)**: v1(HeroGrade6 가정)에서는 "가챠/비가챠예산 배율이 1.7배→1.23배로 완화된다"고 보고했다. **본 v2(구조 정합 재검증 결과 HeroGrade4가 맞다는 결론)에서는 정반대 방향이 나온다**:
- 비가챠 예산(승급+마스터리) = 0.22+0.30 = **0.52**(구 0.64보다 오히려 **축소** — v1의 0.88과는 반대 방향)
- 가챠 기여(1.08) ÷ 신규 비가챠 예산(0.52) = **2.08배**(구 1.7배 대비 **악화**, v1이 예상했던 1.23배 완화와는 정반대)
**원인**: v1은 "히어로가 영원히 1명이니 최댓값(HeroGrade6)을 목표로 삼아야 한다"는 잘못된 전제 위에서 마스터리 총량을 크게 잡았다. 구조가 정정되며 "현재 활성 마스터리 총량"이 실제로는 더 작아졌고(0.42→0.30, 원래 잘못 이식된 값보다도 작음), 그 결과 가챠의 상대적 비중이 더 커졌다. **PD는 N-1(가챠 raw값 유지) 확정 당시의 "1.7배 초과 감수"라는 판단이, 이번 구조 정정으로 실제로는 2.08배까지 벌어진 상태에서 여전히 유효한지 재확인이 필요하다** — N-1 재론을 요구하는 것은 아니나(C36), 이 사실을 인지하지 못한 채 다음 판단을 내리시게 할 수 없어 최우선으로 고지한다.
### 5-3. 예비 등급(HeroGrade5·6) 활성화 시 참고치(승계, 재계산 불요)
향후 히어로 확장 또는 등급승급 메커니즘 도입 시 그 히어로가 HeroGrade5·6이라면 승산항이 각각 1.76(HeroGrade5, +2.3%)·1.96(HeroGrade6, +14.0%)까지 오른다 — v1 §6-1 표 값 그대로 유효(구조만 "다음 히어로의 잠재치"로 재해석).
---
## 6. 검증 시나리오
| # | 시나리오 | 기대 결과 |
|---|---|---|
| 1 | 신규유저 기준선 무결성 | `Data.HeroGrade` 기본값 4, `MasteryAttackRatio()=0`(미투자) — `FinalAttack()` 불변 |
| 2 | 현재 히어로로 Node2 MasteryGrade3(만렙) 도달 | `MaxGradeOf(2,4)=3` — 이후 강화 버튼 "MAX", `ValueAt(2,4,3)=0.15` 정상 반환(HeroGrade4 슬라이스만 조회, 5·6 슬라이스 오염 없음) |
| 3 | 기존 세이브 마이그레이션(v1 배포 이후 유저 가정) | `Load()``HeroGrade<=0`이면 4로 채움 — 기존 진행도(MasteryGrade 값) 무손실 |
| 4 | (미래 대비) HeroGrade=6인 신규 히어로 슬롯 시뮬 | `ValueAt(2,6,5)=0.35`·`MaxGradeOf(2,6)=5` 정상 반환 — 코드 변경 없이 테이블 조회만으로 성립 |
| 5 | Node1 스키마 호환 확인 | `n_HeroGrade` 컬럼 추가 후 기존 6행 전부 조회 결과 무변화(모든 행 동일 HeroGrade 값이라 실질적으로 단일 슬라이스와 동치) |
---
## 7. PD 확인 항목
1. **🔴 현 히어로 HeroGrade 배정값**: **4(최저) 채택 권고** — 최고등급 접근 최저 문턱·F2P 장르 관행·다영웅 확장 헤드룸(§2). 대안 5·6은 §2-3 기각.
2. **🔴★ N-1 재확인(최우선 고지, §5-2)**: 가챠/비가챠 예산 배율이 구조 정합 재검증 결과 1.7배→**2.08배로 악화**한다는 사실 인지 필요(v1의 "1.23배 완화" 보고와 정반대 방향 — 정직 정정).
3. (Q2 연계, 결정 아님) **Node1 편입 여부**는 PD Q2 답변 이후 별도 처리 — 편입 시 개발팀장의 계열10~16 등급 대응 재추출이 선행 필요(§3).
---
## 8. 리스크
| ID | 리스크 | 심각도 | 내용 |
|---|---|---|---|
| R-E4(신규) | HeroGrade5(계열94/74) 값이 🟡 추정(선형 외삽) | 낮음(현재 비활성) | §4-1·4-2 — 실제 게임에 영향 없음(HeroGrade4만 활성), 활성화 전 재추출 권고 |
| R-E5(신규) | v1 배포 이후 세이브 마이그레이션 누락 시 `HeroGrade=0` 잔존 | 중(구현 주의) | §1-3 — `Load()` 기본값 채움 로직 누락 시 `MaxGradeOf(node,0)`가 항상 0을 반환해 마스터리 전체가 잠기는 회귀. 검증 시나리오3 필수 |
| R-E6(신규, 승계) | 승산항 하락(0.42→0.30)으로 가챠 상대 비중 심화(§5-2) | 정보성(PD 확인 항목 2로 격상 처리) | N-1 재검토 필요성 자체가 리스크로 등재되기보다 PD확인으로 직행 |
---
## 9. 기각안 (C32 — v1 전체 승계 + 신규 3건)
**v1 기각안 전체 무변경 승계**(v1 §1-5·§2-3·§10 참조 — M-1~M-3·m-3 보강분 포함).
**v2 신규 기각안**: §2-3 표 3건(HeroGrade6·5·(a)B1승급매핑) — 상술.
**v2 보강분 신규 기각안(plan-auditor M-2·m-1 반영)**: HeroGrade5(계열94/74) 8행을 🟡 추정 표기 유지한 채 CSV에 수록 — 기각(§1-2). `f_Value`/`l_Cost`는 순수 수치 컬럼이라 CSV 자체로는 "추정"이라는 사실을 전달할 방법이 없어, 실측으로 오인될 위험이 표기 유지만으로는 해소되지 않는다 — 재추출 전까지 CSV에서 완전 제외하는 편이 "원작 실측만 CSV에 수록"이라는 기존 관행(Node1)과 정합.
---
## 10. 변경 이력 (P16)
| 일시 | 작성 | 변경 | 근거 |
|---|---|---|---|
| 2026-08-23 | balance-designer | v1 완료 + 보강 5건(대표 계열 95/75 고정 채택) | plan-auditor 조건부통과 |
| **2026-08-23** | **balance-designer** | **v2 신규 — 구조 피벗**(대표 계열 고정 전제 폐기). ①CSV `n_HeroGrade` 컬럼 신설·HeroGrade4·6 16행 수록(HeroGrade5 8행은 재추출 전까지 보류, §1) ②(a)B1승급매핑을 원작 실측(hero_star.csv quality≠star 직교 축)으로 기각·현 히어로 HeroGrade=4 고정 배정 채택(§2) ③Node1 편입 보류·스키마만 호환(§3) ④N-1 전제 재이동 발견 — v1의 "1.23배 완화" 예상이 구조 정정 후 "2.08배 악화"로 반전, 최우선 고지(§5-2) | PD 직접 지시(대화로그 §98) — "영웅 등급별로 맞춰"(다영웅 로드맵 근거) |
| 2026-08-23 | plan-auditor | 모드A 감사 수행(트랙 분리) | — | **PD 상신 트랙**(구조·수치·기각근거·N-1반전 고지) 조건부 즉시 적격 통과. **구현 인계 트랙**은 §1-3 코드 diff 전사 오류 정정 후 적격(Critical 3·Major 1·Minor 2 — "설계는 정확·grep 한 번으로 걸러졌을 항목") |
| 2026-08-23 | balance-designer | plan-auditor 지적 5건 반영(같은 v2 내 확정, §1-3 전면 재작성) | 초안 | **C-1**(로더 인덱스 실제 CSV 순서로 정정 — 최초본은 `HeroGrade=int.Parse(c[1])`가 문자열 `s_StatKey`를 파싱해 즉시 FormatException)·**C-2**(`MasteryAttackRatio()` 누락분 추가 — "메서드 4개" 선언에 3개만 나열했던 오류)·**C-3**(`SurvivalLobbyController.SkillMastery.cs:156` `ValueAt` 직통 호출 1줄 diff 추가 + "UI 변경 0" 주장을 실측 반증 결과로 정정)·**M-1**(`CumulativeCostAt` 3곳 전체 명시 diff + `c.Length<5→<6` 가드)·**M-2·m-1**(HeroGrade5 8행 CSV 제외 결정 — §1-2·§4에 반영, §1-1 유추에 🟡 태그) | plan-auditor 조건부통과 회송 — 구현 인계 전 필수 정정 |
| — | 원칙 | **시그니처 변경 diff 작성 시 해당 심볼 전 호출부 grep 재확인 절차**(감사 권고 수용) | 층간 교차표 필수화(v1 §6-1 원칙) 옆에 나란히 두는 상시 체크리스트 항목(§1-4, 차기 계승) |
---
## 11. 후속 조치 (본 v2 범위 밖)
1. **PD 확인 2건 상신**(§7) — HeroGrade 배정값·N-1 재확인. 최우선(§5-2 악화 방향 고지 포함).
2. **개발팀 구현**: §1-3 코드 diff(v1 §3 per-node MaxGrade 인프라 위 확장) + CSV 갱신(16행, `n_HeroGrade` 컬럼 — HeroGrade5는 재추출 후 별도 추가). C6-1 백업 필수(v1 R-E2 승계).
3. **Node1 재추출 요청 상신**(§3, PM 경유) — 계열10~16의 등급 대응 여부, Q2 답변과 병행 처리 권고.
4. **HeroGrade5(계열94/74) 정밀 재추출**(R-E4) — 현재 🟡 추정치, 비활성 상태라 낮은 우선순위.
5. **v1 문서 배너 갱신**: 이미 본 문서 상단에 반영 완료.
6. **B3 본체 v3 §8-4 갱신 필요**(§5-2 연계) — v1 후속조치#6이 "3.06배(HeroGrade6 가정)"로 예고했던 것을 "2.08배(HeroGrade4 확정)"로 대체 갱신.