BurningTimesAi/개발실/프로젝트_숙지/수상한잡화점/09_카드시스템_아키텍처_v1.md

11 KiB

09. 카드시스템 아키텍처 v1

  • 작성일: 2026-04-14
  • 작성자: 개발실장 (Explore 에이전트 분석 위임)
  • 목적: 기획실이 관리하는 카드(G1~G5 총 311장) 이 코드상 어떻게 표현·실행·획득되는지 확정
  • 스코프: Assets/Script/Table/Tables/table_cardlist.cs, Assets/Script/InGame/Actor/Actor.cs, Assets/Script/My/MyClass.cs
  • 선행 문서: 08_전투시스템_SOT_v1.md

1. 카드 데이터 모델

1.1 eCardType Enum

table_cardlist.cs:123~450G[1-5]_<효과명> 형태로 311개 정의.

등급 개수 대표
G1 (Common) 112 G1_AddPotion ~ G1_ShieldIncreaseAttackPower
G2 (Uncommon) 73 G2_CriUpAfterCri ~ G2_RangeDodgeUp
G3 (Rare) 51 G3_CritDmgUp ~
G4 (Hero) 43 G4_ZeroShieldCritRate ~
G5 (Legend) 32 G5_RemoveNormalSkills ~ G5_RandomLightning_N
합계 311 eCardType.Max sentinel

명명 규칙: G[1-5]_<CamelCase>. 기획실 SOT 엑셀 행과 1:1 매핑.

1.2 CardListTableData 필드 (table_cardlist.cs:7~120)

필드 타입 암호화 용도
e_CardType enum 식별자
n_ID int ObscuredInt 고유 ID
e_CardGrade eGrade 등급
s_Value1~3 / f_Value1~3 string / float float만 Obscured 수치 (문자열/런타임 이원)
s_LvUpValue1~2 / f_LvUpValue1~2 string / float float만 Obscured 레벨업 가중치
e_LvUpType enum 레벨업 적용 방식
b_Percent, b_LvUpValue*Percent bool % 해석 플래그
s_Projectile / e_ProjectileAttackType / n_ProjectileCount / f_ProjectileDelayTime Obscured(int/float) 투사체
n_StatusOptionSetID int Obscured table_StatusOptionSet 조인
n_UseDmg int Obscured 피해 계산 방식 (0=고정, 1=%비율, 2=배수) (확인 필요)
n_Desc, n_LvUpDesc int 텍스트 테이블 키

핵심 관찰: 수치는 ObscuredFloat/Int로 안티치트 적용. 메타(enum, 플래그)는 평문. 수치 변경은 테이블만 수정하면 코드 무수정 — 기획실 밸런싱 규칙("효과 종류 변경 금지, 수치만 조정")과 구조적으로 일치.

1.3 eLvUpType (table_cardlist.cs:110~122)

Use_LvUpValue1_To_Value1   // f_Value1 누적
Use_LvUpValue1_To_Value2
Use_LvUpValue1_To_Value3
Use_LvUpValue1_To_ProjectileCount
Heal_Multi / Heal_Mul       // 즉시 회복 효과
GetGold                     // 즉시 골드

레벨업 수치 적용 (CardListTableData.cs:96~100):

int Get_CardLv() => Mathf.Max(0, MyValue.sdata.Get_CardLv(n_ID) - 1);
float Get_LvUpValue1(eLvUpType t) =>
    e_LvUpType == t ? f_LvUpValue1 * Get_CardLv() : 0;

선형 누적: Value_final = Value_base + Level * Coefficient.

1.4 CardSkillData 런타임 인스턴스 (MyClass.cs:147~250)

public class CardSkillData
{
    public CardListTableData m_Data;
    Actor m_Actor;
    public bool StartBattle;
    public int TargetIdentity;
    public int UseCount;
    public int UseCount_Acc;
    public float m_LifeTime;                   // 주기 쿨타임
    public Dictionary<int, int> dic_identity;  // 대상별 추적
}

2. 카드 효과 실행 구조

2.1 OnEvent_* 진입점 맵 (~258개 분기)

Actor.cs 전반:

메서드 라인 발동 분기 수
OnEvent_AddSkillCard() 1799 카드 획득 직후 1회 ~80
OnEvent_RunSkillCard() 1655 획득/라인변경 반복 ~50
OnEvent_Kill_Hitter() 2244 적 처치 ~20
OnEvent_Cri_Hitter() 2616 크리 발동 ~15
OnEvent_Avoid() 3001 회피 성공 ~12
OnEvent_NoMiss_Hitter() 2442 회피 실패 ~8
OnEvent_MyShield_Hit() 3131 쉴드 피해 ~10
OnEvent_MyShield_Break() 3145 쉴드 소진 ~5
OnEvent_GetDmg() 3202 피해 수신 ~15
OnEvent_PlayAttack() 2022 공격 발동 ~20
OnEvent_LvUp() 2917 레벨업 ~25
OnEvent_MeetNode() 2746 노드 진입 ~5
OnEvent_MakeMob() 3408 몬스터 생성 ~3
OnEvent_Hit() 3218 피해 적용 후 ~10

2.2 효과 적용 3가지 패턴

  • A. 조건 분기 (if-switch) — 카드 1장당 1개 분기 (Actor.cs:1801~1830 등). 신규 추가 시 코드 수정 필요.
  • B. 데이터 주도 (테이블 참조)CardSkillData.Add(...) 에서 Set_Buff(eStat, value) 로 일반화 (MyClass.cs:161~180). 이 경로는 enum 케이스만 추가하면 됨.
  • C. 주기형 (m_LifeTime)G1_MagicMissile 처럼 쿨타임 기반 (MyClass.cs:165~166). Update()에서 감소.

2.3 트리거 분류표

트리거 예시 카드 위치
즉시 (획득 시) G1_FireMagicMissiles, G1_InstantFullHeal OnEvent_AddSkillCard
온 공격 G1_FirstAttack, G1_FifthAttackBack OnEvent_PlayAttack
온 피해 G1_HealOnRangedHit OnEvent_GetDmg
온 킬 G1_CastMagicMissilesOnBackKill OnEvent_Kill_Hitter
온 크리 G1_ThirdCritDamage OnEvent_Cri_Hitter
온 회피 G1_LifeStealOnDodge OnEvent_Avoid
온 노드시작 G2_HeroSkillFullHeal OnEvent_RunSkillCard
영구 스탯 G1_AddMaxAttack CardSkillData.Add → Set_Buff
1회성 버프 G1_DodgeNextNAttacks OnEvent_AddSkillCard
주기 효과 G1_MagicMissile m_LifeTime

3. 카드 획득 · 보유 · 제거

3.1 획득 경로

노드 클리어 → Co_AllKill() → act_EndNode()
 → SelectCardUI.Set_Cards() [BattleCard.cs:37]
 → BattleCard.OnClick()
 → InGameInfo.Ins.Add_Card(CardListTableData)
 → Actor.Add_CardSkill() [Actor.cs:337]
 → dic_Card[eCardType] = CardSkillData
 → OnEvent_AddSkillCard()

추가 경로:

  • 성소: Actor.Add_Card_Random() (Actor.cs:4474)
  • 장비 특수효과: Set_Equipment()list_SkillId (Actor.cs:3773)
  • 상태이상 연쇄: GetSkillCard_N (Actor.cs:3866~3871)

3.2 보유 구조

  • 저장소: Dictionary<eCardType, CardSkillData> dic_Card카드 타입별 1장
  • 조회: IsObtainCardSkill(eCardType) O(1)
  • 슬롯 상한: 코드상 무제한(확인 필요) 기획 UI 상한 존재 여부
  • 중복 방지: if (!dic_Card.ContainsKey(...)) (Actor.cs:4487)

3.3 제거 경로

현재 코드상 카드 제거 경로 없음Actor.Set() (Actor.cs:148) 에서 런 리셋 시 dic_Card.Clear() 만 존재.


4. 카드 효과 카탈로그 (대표 샘플)

4.1 카테고리별

카테고리 수량 추정 대표
공격력 증강 ~25 G1_AddMaxAttack, G3_BacklineDoubleDmg, G5_FlatDamageMinMaxEqual
피해 감소/무효화 ~18 G2_ReduceMeeleEnemyDamage, G2_ActivateInvincibleShieldOnKill, G1_ReflectOnRangedDodge
회피/회피율 ~12 G1_DodgeNextNAttacks, G2_FirstMeleeAlwaysEvade, G1_MaxDodgeWhenHpBelow
크리 ~15 G2_CriUpAfterCri, G1_ThirdCritDamage, G2_NextSevenHitsCritical, G5_EvadeCrit
쉴드 ~20 G1_ShieldOnRangedDodge, G1_MaxShieldUpOnLevelUp, G1_SpringShieldToHP
회복/생명력 ~22 G1_HealOnRangedHit, G1_AngelFeatherHeal, G1_StopExploreHealHP
상태이상 부여 ~15 G1_EnemySpawnStunChance, G1_StunAllMeleeEnemies, G5_EvadeStun_N
특수 투사체 ~35 G1_MagicMissile, G1_ThunderOnFifthHit, G4_StartArrowRainRangedImmune, G5_CritTriggerMeteor
스킬 카드/특수 ~18 G1_GetRandomRareOrEpicSkill, G1_NextLevelChooseTwoSkillCards, G1_CastReaperOnLevelUp
수집/보상 ~12 G1_GainGoldOnSanctuaryFind, G1_XpGainOnCritKill, G1_CampRechargePotion
방어/면역 ~10 G2_ActivateInvincibleShieldOnKill, G5_PotionDamageImmunity

4.2 등급별 경향

  • G1 112장 (36%) — 기초재. 모든 런에서 자주 획득.
  • G2 73장 (23%) — 조건부/조합 효과.
  • G3 51장 (16%) — 라인·배수·크리 특수.
  • G4 43장 (14%) — 실드·동일대상·면역 연계.
  • G5 32장 (10%) — 투사체·무적·즉사 최상위 시너지.

G1 비중이 높은 것은 기획 의도(풀초기/보편 효과)로 해석 가능.


5. 카드시스템과 밸런싱 훅

5.1 데이터 입력 흐름

기획실 .xlsm
  → (익스포트 도구) → JSON/CSV
  → (코드 생성) → table_cardlist.cs (자동)
  → (Awake) → CardListTableData 싱글톤
  → (전투 중) → CardSkillData.m_Data → Get_*Value*()

코드상 수치 하드코딩은 확인되지 않음 — 모든 수치는 테이블 필드. → 밸런싱은 테이블만 수정하면 된다는 구조적 전제 성립.

5.2 핫 리로드

  • 현재 불가 — 싱글톤 Awake에서 초기화, 런 중 반영 안 됨. 재시작 필요.
  • 개선안: ScriptableObject / JSON 원격 동기화 / 기획실 대시보드 연동 — Phase 1+ 고려.

5.3 기획실 워크플로 접촉점

단계 파일/도구 비고
효과 설계 기획 엑셀 효과 종류 변경 금지 (규칙)
수치 입력 f_Value1~3, f_LvUpValue1~2
익스포트 도구 경로 (확인 필요) 문서화 필요
코드 생성 table_cardlist.cs 자동
QA Unity 런타임
시뮬레이터 검증 기획실 매크로 코드와 공식 일치 여부 (확인 필요)

6. 리스크 · 이슈

6.1 조건 분기 폭발 (~258 위치)

  • 단일 카드 효과가 여러 OnEvent_*에 분산 → 신규 추가 시 수정 누락 위험.
  • 개선안: interface ICardEffect + CardEffectRegistry 디스패처 패턴으로 효과별 객체 분리. (장기 리팩토링 과제)

6.2 하드코딩 상수 (08 문서와 중복)

  • 3.69 (회피 분모) Actor.cs:623
  • 0.9f (크리·회피 상한) MyValue.cs:23
  • eCardType.Max sentinel — 선택적 테이블화 대상

6.3 구현 누락 후보

  • G5 32장 중 일부 미구현/주석 처리 가능성 — (확인 필요) TODO/FIXME/주석 grep
  • G1_NullifyDamageEveryNHitsDmg_Immune 스탯 순서 버그 가능성

6.4 시뮬레이터 동기화

항목 코드 시뮬 상태
피해 15단계 Actor.Get_Dmg 엑셀 매크로 대조 필요
크리 확률 RandomTrue + 강제 고정% 대조 필요
회피 상한 PC 0.9f ? (확인 필요)
카드 효과 트리거 분산 테이블 큰 차이

6.5 성능

  • ObscuredInt/Float 복호화·재암호화 비용이 Get_Damage 호출마다 발생. 크리 판정/피해 계산에서 프레임당 수십 회. → Profiler 측정 필요.

6.6 버그 냄새

  1. 중복 획득 방지 — 특수 아이템 list_SkillId 경로 (확인 필요)
  2. OnEvent 순서 의존성 — AddSkillCardRunSkillCard 순서
  3. G5 카드 스킵 로직 — 이미 획득 상태 체크 누락 가능성

7. B-2 완료 조건 체크리스트

필수

  • 311개 전체 eCardType 하드카피 리스트 (문자열 ID)
  • 각 카드의 대표 분기점 매핑 (OnEvent_*)
  • G5 미구현/주석 목록 확보
  • table_cardlist 필드 ↔ 엑셀 컬럼 1:1 매핑 확정
  • eLvUpType 전 케이스 동작 테스트
  • IsObtainCardSkill 258 호출 위치 전수 지도 (누락 확인)

선택

  • 기획 엑셀 매크로 방정식 덤프 (코드 수식과 대조)
  • 카드 의존성 그래프
  • ObscuredFloat 프로파일링

8. 변경 이력

버전 일자 작성자 내용
v1 2026-04-14 개발실장 (Explore 위임) Phase 0-B-2 초안