TIL - 몬스터 파츠 드랍 구조 변경

Kyu_·2026년 7월 9일

33주차

목록 보기
4/4

몬스터 파츠 드랍: 레어도 태그 + 랜덤 파츠 풀

목표

몬스터 드랍테이블에서 파츠를 "특정 PartDefinition 지정" 방식이 아니라 "레어도만 지정 → 실행 시점에 전체 파츠 풀에서 랜덤 1개 선택 + 해당 레어도 부여" 방식으로 뽑는다.

확정된 최종 구조

  • FNSRewardDropRow/FNSRewardDropResult에서 PartDefinition(TSoftObjectPtr<UNSPartDefinition>) 필드는 완전히 제거. Part 타입 행은 항상 "전체 파츠 풀에서 랜덤 선택" 방식만 사용 (몬스터별로 드랍 파츠 종류를 다르게 할 계획이 없다고 기획 확정 → 특정 파츠 못박는 경로 자체를 없앰).
  • 레어도는 ENUM이 아니라 태그로 받는다: FGameplayTag RarityTag (Part.Rarity.Common/Rare/Epic/Legendary). 이유:
    • 같은 행의 다른 필드들(DropGroupTag, RewardTypeTag, CurrencyTag, AugmentPoolTag, HealPotionTag)이 전부 태그라서, Rarity만 한글 UENUM이면 이질적임.
    • RewardTypeTag(보상 종류 축)와 Rarity(등급 축)는 서로 다른 축이라 하나의 태그 계층으로 합치지 않음 — Reward.Type.Part.Common 식으로 만들면 RewardTypeTag == ... 정확 일치 비교 5곳(NSRewardHandler.cpp, NSRewardDropResolver.cpp)이 전부 깨짐.
    • "미설정 시 기본값" 문제: ENSPartRarityNone을 추가하는 것도 검토했으나 기각. None을 추가하면 그 값이 실수로 PartData.CurrentRarity까지 흘러갔을 때 NSPartEquipComponent.cpp의 등급업 로직(static_cast<ENSPartRarity>(uint8(Rarity)+1))이 enum 범위 밖 값을 만들 수 있어 UI switch문에서 미정의 동작 위험이 있음. 태그는 미설정 시 자연히 "invalid 태그"가 되므로 별도 sentinel 값이 필요 없음.
  • 태그 ↔ enum 변환은 NSPartUtils::ResolveRarityFromTag(RarityTag, OutRarity)가 전담 (변환 실패 시 false 반환 → 호출부가 드랍을 안전하게 스킵).
  • ENSPartRarity enum 자체는 원래대로 유지 (Common/Rare/Epic/Legendary, None 없음). 파츠 실제 로직(업그레이드/상점/세이브)은 계속 이 enum을 그대로 사용, 영향 없음.
  • 랜덤 풀 선택은 기존 패턴 재사용: NSPartEquipComponent.cpp:631~647(상점 재고 생성)와 동일하게 DataSS->GetAllPartRows() 순회 + 인덱스 랜덤 선택.

변경 파일 (전부 완료)

  1. Source/NeoSanctum/Data/Part/NSPartTypes.h

    • 변경 없음 (None 추가했다가 되돌림. 최종적으로 원본 그대로: Common/Rare/Epic/Legendary만).
  2. Source/NeoSanctum/Tag/NSGameplayTags_Part.h / .cpp

    • Part.Rarity.Common / Rare / Epic / Legendary 4개 태그 신규 추가.
  3. Source/NeoSanctum/Progression/Part/NSPartUtils.h / .cpp

    • ResolveRarityFromTag(const FGameplayTag&, ENSPartRarity&) -> bool 추가. 태그 4개를 순서대로 비교해 매칭되는 enum 값을 반환, 매칭 안 되면 false.
    • .h#include "GameplayTagContainer.h" 추가, .cpp#include "NeoSanctum/Tag/NSGameplayTags_Part.h" 추가.
  4. Source/NeoSanctum/Data/Reward/NSRewardTypes.h

    • FNSRewardDropRow/FNSRewardDropResult 둘 다: PartDefinition 필드 삭제, RarityTag(FGameplayTag, meta=(Categories="Part.Rarity")) 추가.
    • 이제 이 파일에서 ENSPartRarity/UNSPartDefinition을 안 쓰므로 관련 include·forward-declare 제거.
  5. Source/NeoSanctum/Data/Reward/NSRewardDropResolver.cpp

    • ApplyDropRowToResult: OutResult.PartDefinition = ... 삭제, OutResult.RarityTag = Row.RarityTag; 추가. 리졸버는 태그를 그대로 전달만 함(파츠 세부 로직에는 관여하지 않음 — 책임 분리 유지).
  6. Source/NeoSanctum/Progression/Reward/NSRewardHandler.cpp

    • 상단 #include "Engine/AssetManager.h" 제거 (이 파일에서 UAssetManager 쓰는 곳이 없어짐).
    • HandlePartDropResult: DropResult.PartDefinition 참조하던 가드/로그 전부 제거. PartData.IsValid() 결과로만 판별.
    • MakePartDataFromDropResult: UNSDataSubsystem::Get(World)->GetAllPartRows()에서 랜덤 픽 → NSPartUtils::ResolveRarityFromTagDropResult.RarityTag 변환(실패 시 무효 PartData 반환) → PartData.CurrentRarity에 반영.
    • 주의(수정 완료): ValueRange 롤링은 RandomStream.FRandRange(MinValue, MaxValue)를 써야 함. 중간에 RandRange(int32 전용 오버로드)로 잘못 타이핑되어 float가 int로 잘리는 버그가 있었으나 FRandRange로 수정함.
  7. DataTable(디자이너 작업, 코드 범위 밖)

    • 드랍테이블 Part 타입 행에는 이제 RarityTag만 채움 (Part.Rarity.Common 등). PartDefinition 열 자체가 없어졌으므로 입력 불필요.

0개의 댓글