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

34 KiB
Raw Permalink Blame 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):

  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])로 그 문자열을 파싱해버리는 자기모순이 있었다 — 아래는 실제 인덱스로 재작성):

- 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:

- 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 정정 — "동일 패턴" 뭉개지 않고 루프 내부까지 전부 명시):

- 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 재대조):

  // SurvivalMetaData
+ /// <summary>현재 히어로의 고정 등급(4/5/6, 원작 heroskillattr 계열 대응). 다영웅 확장 시
+ ///   히어로별로 이 필드가 각각 다른 고정값을 가지게 된다(원작 hero_star.csv quality축과 동일 성격).
+ ///   본 필드는 성장/승급으로 변하지 않는다 — B1 PromotionStar와는 독립인 축(§2 근거).</summary>
+ public int HeroGrade = 4;
  // ★ 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)직통 호출하는 지점이 실제로 존재했다):

  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.Datapublic 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 감사 수행(트랙 분리)
2026-08-23 balance-designer plan-auditor 지적 5건 반영(같은 v2 내 확정, §1-3 전면 재작성) 초안
원칙 시그니처 변경 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 확정)"로 대체 갱신.