← 자료실/designsLOW

D8 (P1) stat_key 정합성 검증 리포트

designs/d8-stat-key-validation-2026-04-21.md

D8 (P1) stat_key 정합성 검증 리포트

작성: 2026-04-21 game-designer 근거: scaffold sync audit 2026-04-21 D8 (P1) / designs/prestige_items.tsv v1.0 APPROVED / designs/prestige_effect_matrix.tsv / designs/prestige_system_spec.md v1.0 APPROVED / designs/sprint-03-idle-research.md v1.0 APPROVED / 메모리 project_sprint02_w1_prestige_items.md, project_sprint02_w2_prestige_tech_sketch.md 상태: v1.0 APPROVED (PD 승인 2026-04-21) 범위: 읽기 전용 교차 검증. TSV·수치 시트·코드 편집 0건. 본 문서 신규 생성 1건만.

PD 승인 결정표 (2026-04-21)

결정내용코멘트
N1 BuildSummary 네이밍B안 = 외부 표준명 + Importer 매핑Sprint 03 리서치 문서 전반에서 IdleRateMul/GoldMul/ExpMul/DropMul 그대로 사용. PrestigeItemImporter 가 TSV stat_key 컬럼을 외부 표준명으로 매핑(1:1 Dictionary). TSV는 game-designer 관점의 가독성(% 접미사) 유지. BuildSummary 필드명은 Calculator 가독성 우선. game-designer 권장안 채택
N2 Sprint 03 문서 소급 수정승인 — sprint-03-idle-research.md §2.1 / §5 축3, 축8 의 "#24 IdleCapHours" → TSV 실제 "#25 IdleCapHoursFlat (FlatStat, 별도 경로)" 로 정정문서만 수정, 코드 0. IdleCapHoursFlat (item_id=25) 은 PrestigeCalculator.GetIdleCapHours(state) 헬퍼 통해 24 + 2 × min(level, 5) 수식으로 파생 (Lv5 상한 34h). 곱연산 BuildSummary와 분리 경로

Sprint 02 W2 착수 조건 해소: 두 결정 완료로 code-lead는 2026-05-18 Sprint 02 킥오프 시 PrestigeItemImporter + PrestigeCalculator.BuildSummary + GetIdleCapHours 헬퍼 즉시 착수 가능.

Importer 매핑 표 (N1-B 결과):

TSV stat_keyBuildSummary 필드effect_type경로
IdleYieldPctIdleRateMulMultiplier (%)곱연산
GoldGainPctGoldMulMultiplier (%)곱연산
ExpGainPctExpMulMultiplier (%)곱연산
DropRatePctDropMulMultiplier (%)곱연산
IdleCapHoursFlat헬퍼 경유 GetIdleCapHours(state)FlatStat (h)별도
나머지 25개 stat_key원본 그대로—곱/합연산 (TSV 컬럼)

§0 TL;DR (PD 1분)


§1 stat_key 마스터 테이블 (30행)

TSV 원본 기준. 컬럼 5개만 추출 (전체 15컬럼 중).

item_idtabdisplay_name_krstat_keyeffect_type
0Offense공격력AttackPowerPctPercentStat
1Offense치명타 확률CriticalRatePctPercentStat
2Offense치명타 피해CriticalPowerPctPercentStat
3Offense공격 속도AttackSpeedPctPercentStat
4Offense스킬 쿨다운 감소SkillCoolDownPctPercentStat
5Offense관통력PenetrationPctPercentStat
6Offense원소 피해ElementalDamagePctPercentStat
7Offense보스 피해 증폭BossDamagePctPercentStat
8Offense다굴 피해 증폭PackDamagePctPercentStat
9Offense장비 옵션 증폭EquipOptionAmpMultiplier
10Defense최대 체력MaxHpPctPercentStat
11Defense방어력DefensePowerPctPercentStat
12Defense회피율DodgeRatePctPercentStat
13Defense받는 피해 감소DamageReductionPctPercentStat
14Defense체력 재생HpRegenPctPercentStat
15Defense흡혈LifeStealPctPercentStat
16Defense받는 치명 확률 감소CritDodgePctPercentStat
17Defense수호자 공격력GuardianAtkPctPercentStat
18Defense수호자 체력GuardianHpPctPercentStat
19Defense영웅 부활 횟수ReviveCountFlatFlatStat
20Utility드롭률DropRatePctPercentStat
21Utility매직파인드MagicFindPctPercentStat
22Utility골드 획득량GoldGainPctPercentStat
23Utility경험치 획득량ExpGainPctPercentStat
24Utility방치 수익 증폭IdleYieldPctPercentStat
25Utility방치 캡 시간IdleCapHoursFlatFlatStat
26Utility프레스티지 포인트 획득량PpGainPctPercentStat
27Utility마일스톤 보상 증폭MilestoneBonusPctPercentStat
28Utility층 입장권 할인StageTicketDiscPctPercentStat
29Utility수호석 포인트 확률GuardianShardRatePctPercentStat

§1.1 중복·오타 검사


§2 Sprint 03 요구 필드 매핑

Sprint 03 sprint-03-idle-research.md §2.3(옵션 B 집계 공식), §5 축8, §9 CP-I4 가 기대하는 PrestigeCalculator.BuildSummary 필드를 TSV stat_key 와 교차.

§2.1 1:1 매핑 테이블

Sprint 03 기대 필드 (BuildSummary.*)용도 (수식에서)대응 TSV item_idTSV stat_keyeffect_type매핑 상태
IdleRateMulppGained = elapsedSec/3600 × maxFloor × 0.05 × IdleRateMul (§3.2 C안)24IdleYieldPctPercentStatYELLOW — 이름 불일치
GoldMulgoldTotal = kills × avgGold × GoldMul (§2.3)22GoldGainPctPercentStatYELLOW — 이름 불일치
ExpMulexpTotal = kills × avgExp × ExpMul (§2.3)23ExpGainPctPercentStatYELLOW — 이름 불일치
DropMul (추정, 리서치 문서에서 함축 — dropTable.RollDrops 확률 곱)드롭 수량 보정20DropRatePctPercentStatYELLOW — 이름 불일치
IdleCapHours (스칼라, 곱연산 아님)cappedSec = min(elapsed, IdleCapHours × 3600) (§2.1, §2.4)25IdleCapHoursFlatFlatStatGREEN — 존재 + 타입 정합 (§3 별도 경로)

§2.2 Sprint 03 §5 축8 재인용

축4: PrestigeCalculator.BuildSummary — Sprint 02 W2 시그니처 고정. IdleRateMul 필드 존재 여부 확인 필요 → Sprint 02 W2 Stat 확정 후 교차 확인 축8: IdleCapHours 프레스티지 아이템 (#24) — IdleRewardCalculator 가 min(elapsed, cap) 적용. cap 은 PrestigeCalculator.CalculateLevel + 사전 정의 공식에서 파생

발견: Sprint 03 문서가 "프레스티지 아이템 #24" 를 IdleCapHours 로 지칭하나, 실제 TSV 기준 #24는 IdleYieldPct (방치 수익 증폭), #25가 IdleCapHoursFlat (방치 캡 시간). 두 항목이 Utility 탭에서 연달아 배치되어 혼동 가능. Sprint 02 W2 Importer 작성자 + Sprint 03 IdleTicker 작성자는 item_id 가 아닌 stat_key 로 참조 해야 혼동 방지.

§2.3 D1 제안 — BuildSummary 필드명 규약 2안 비교

D1-A안: TSV stat_key 그대로 (IdleYieldPct, GoldGainPct, ExpGainPct, DropRatePct)

D1-B안: BuildSummary 별도 네이밍 + Importer 내부 매핑 (IdleRateMul ← IdleYieldPct 등)

  // PrestigeCalculator.BuildSummary (Sprint 02 W2 신설 시)
  public readonly struct BuildSummary {
      public float AttackMul;      // ← AttackPowerPct 합산 → 1+sum
      public float IdleRateMul;    // ← IdleYieldPct 합산 → 1+sum
      public float GoldMul;        // ← GoldGainPct
      public float ExpMul;         // ← ExpGainPct
      public float DropMul;        // ← DropRatePct
      public int   IdleCapHours;   // ← IdleCapHoursFlat (덧셈, §3 별도 경로)
      // ... 나머지 24개 필드
  }

권장: D1-B안


§3 IdleCapHoursFlat 특수 경로 스펙

§3.1 왜 특수 경로인가

IdleCapHoursFlat (item_id=25) 은 30개 중 유일하게 "시간(h) 절대값" 을 나타내는 FlatStat 타입. 나머지 PercentStat 29개는 전부 1 + Σ(level × per_level × diminish) 식 곱연산 멀티플라이어로 집계되지만, IdleCapHours 는 다음 이유로 별도 경로 필수:

  1. 단위 불일치: PercentStat은 비율(dimensionless), FlatStat은 시간(hours). 같은 스칼라 필드에 곱연산으로 집계 불가.
  2. 수식 역할 다름: 곱연산 필드는 "보너스 배수" 로 피해·보상에 곱해지지만, IdleCapHours 는 "오프라인 계산 상한" 으로 min() 연산의 입력.
  3. 소프트캡 동작 다름: PercentStat은 Lv > 5 시 log diminishing, FlatStat 25번은 FlatSteps 곡선 — Lv 1~5 선형 (+2h/레벨), 이후 투자 불가(하드캡). Sprint 02 W1 prestige_items_README.md 메모리 합의에서 "CapBehavior=Block UI" 로 확정.

§3.2 집계 수식

TSV 값:

level = PrestigeCalculator.CalculateLevel(state.GetPpInvested(item_id=25), softCap=5)
level_clamped = min(level, 5)   // FlatSteps 하드캡
idleCapHours  = 24 + 2 × level_clamped
             = Lv0: 24h / Lv1: 26h / Lv2: 28h / Lv3: 30h / Lv4: 32h / Lv5: 34h

§3.3 Sprint 02 W2 PrestigeCalculator 구현 요구사항

// 신규 헬퍼 (Sprint 03 리서치 §5 축8 에서 이미 예고됨 — "GetIdleCapHours(state) 신규")
public static int GetIdleCapHours(in PlayerPrestigeState state) {
    int pp     = state.GetPpInvested(stat_key: "IdleCapHoursFlat");
    int level  = CalculateLevel(pp, softCap: 5);
    int capped = math.min(level, 5);
    return 24 + 2 * capped;   // base + per_level × level (Flat 가산)
}

§3.4 Sprint 03 IdleRewardCalculator 호출 패턴

// FlushCatchUp() 내부 (Sprint 03 §4.5)
int capHours    = PrestigeCalculator.GetIdleCapHours(prestigeState);   // § 3.3 헬퍼
long cappedSec  = math.min(elapsedSec, (long)capHours * 3600);
// 이후 Phase 1 Bulk + Phase 2 Tail 계산 (Sprint 03 §2.4 옵션 C)

§3.5 광고 48h 확장 (참고)

Sprint 04+ 광고 부스트 도입 시 capHours + adsBonusHours 형태로 가산. Sprint 02 W2 범위 아님. 현 스펙은 광고 적용 전 기본 캡만 처리.


§4 발견된 gap

§4.1 Gap G1 (YELLOW): 네이밍 규약 불일치 (§2 D1 제안으로 해소 가능)

§4.2 Gap G2 (YELLOW → 해소): Sprint 03 리서치 문서 내 "item_id #24" 지칭 오류

§4.3 Gap G3 (정보): 메모리 project_sprint02_w1_prestige_items.md 오기재

§4.4 Gap G4 없음 (GREEN)


§5 Sprint 02 W2 code-lead 착수 전 확인 체크리스트

Sprint 02 W2 Importer / PrestigeCalculator / BuildSummary 작성 착수 전 다음 5개 항목을 PD 승인 → code-lead 시작.


§6 결론


§A 참고: 빌드 시뮬 매트릭스와의 정합성 (sanity check)

prestige_effect_matrix.tsv 10개 빌드의 주요 열(atk_bonus / idle_bonus / drop_bonus) 은 stat_key 추상화된 비율 값으로 정의되어 TSV 컬럼명에 의존하지 않음. 즉 D1-A/B 어느 쪽 결정이든 매트릭스 파일은 수정 불요. game-designer 수치 튜닝 경로(메모리 "Calculator 시그니처 불변 원칙") 유지.


(끝)