리뷰 발견 수정: 게임버그 6 + 시뮬 충실도 3 + 설명/데이터 정정 (Lua↔JS 동기화) #96

Merged
gahusb merged 11 commits from fix/review-findings into main 2026-06-29 21:46:45 +09:00

11 Commits

Author SHA1 Message Date
8a43ca91da fix(data): 설명이 미구현 효과를 주장하던 5장을 실제 동작에 일치
각 카드 설명이 데이터에 없는 효과를 주장하고 있었다. 실제 런타임
동작에 맞게 설명 수정(미구현 메커니즘 구현 대신 설명 정정):
- Malaise(불쾌): "힘 감소+약화+소멸" → 실제는 useAllEnergy+xWeakPerEnergy
  뿐(단일 적 약화, 소멸/힘감소 없음) → "에너지를 모두 사용하고, 사용한
  에너지만큼 적에게 약화를 부여합니다."
- Mirage(신기루): "중독만큼 방어+소멸" → 실제 draw:1 → "카드를 1장 뽑습니다."
- KnifeTrap(칼날 함정): "표창 재사용" → 실제 draw:1 → "카드를 1장 뽑습니다."
- Strangle(목 조르기): "카드마다 체력감소" 부분 미구현 → "피해를 8 줍니다."
- Rage(분노): kind=Power라 damage:4/aoe가 무시되고 powerEffect도 없어
  재생 시 아무 효과 없던 死카드. 데이터의 damage:4/aoe 의도대로
  kind Power→Attack으로 기능화 + "모든 적에게 피해를 4 줍니다."

카드 121장 유지. 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 21:45:24 +09:00
fc03d58ee7 fix(data): Tactician·Adrenaline 잘린 설명 완성 (필드와 일치)
Tactician(전략가) "교활. 을 얻습니다." → gainEnergy:1 반영해
"교활. 에너지를 1 얻습니다." Adrenaline(아드레날린) "를 얻습니다..."
→ "에너지를 1 얻습니다. 카드를 2장 뽑습니다. 소멸." 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 20:50:04 +09:00
ead73b427e fix(deck): useAllEnergy 카드는 코스트감소를 무시하고 전 에너지 소비 (Lua/JS 동기화)
Malaise(불쾌, xWeakPerEnergy)·Skewer(꼬챙이, xDamagePerEnergy) 같은
useAllEnergy 카드는 X 효과가 소비 에너지에 비례하는데, Lua는 코스트
감소(스킬코스트감소·다음스킬무료·전투코스트감소)를 useAllEnergy에도
적용해 소비 에너지가 full보다 줄고 X도 약해졌다(코스트감소가 카드를
약화시키는 역설). JS는 스킬코스트감소만 건너뛰고 combatReduction은
적용해 양쪽이 미묘하게 달랐다.

정답: useAllEnergy는 "전 에너지 소비"이므로 어떤 코스트감소도 무시.
Lua는 3개 감소 조건에 useAllEnergy 제외 추가, JS는 finalCost를
useAllEnergy면 combatReduction 미적용으로. 양쪽 모두 full 에너지 소비로
일치. 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 20:48:29 +09:00
d78049182b fix(deck): DealDamageToAllMonsters를 isAttack 매개변수화 (버스트 평면화)
DealDamageToAllMonsters는 AoE 공격(취약 1.5x·attackPoison 적용)과
Outbreak 독 버스트(평면 피해) 두 용도로 공유되는데, 취약을 항상
적용해 버스트가 취약 대상에 과다 피해를 줬다(JS 미러는 버스트를
평면 applyDamage로 처리 — Lua만 발산). 또한 직전 커밋에서 추가한
attackPoison도 버스트에 적용돼, Envenom+Outbreak 동시 활성 시
버스트→attackPoison→독 적용→또 버스트의 재귀 위험이 있었다.

isAttack 매개변수를 추가해 취약·attackPoison을 공격일 때만 적용:
AoE 공격(ResolveCardEffects)은 true, 버스트는 미전달(평면). JS의
dealToTarget(취약+attackPoison) vs 버스트(평면) 분리와 일치.
산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 20:45:03 +09:00
5f615e30e2 fix(balance): Prepared 시뮬에 blockPerDamageDealtThisTurn 실제 방어 적용 + 설명 정확화
Lua는 Prepared(예비)에서 AddCardBlock으로 실제 방어를 부여하는데, JS
시뮬은 blockGained(통계 카운터)만 증가시키고 addBlock을 호출하지 않아
플레이어가 실제 방어를 못 받았다(시뮬이 방어를 과소집계).

JS도 다른 블록 출처처럼 addBlock 경유로 변경(Lua 동기화). 또한
Prepared 데이터는 discard:1 + blockPerDamageDealtThisTurn뿐(draw 없음)
인데 설명이 "1장 뽑고 1장 버립니다"로 부정확해, 실제 동작(1장 버리고
이번 턴 피해만큼 방어)에 맞게 보강. RED-GREEN 테스트 추가. 88개.
산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 20:40:02 +09:00
222ed92807 fix(balance): firstShivDamageBonus 시뮬을 첫 Shiv에만 적용 (Lua 동기화)
PhantomBlades(환영검: 첫 Shiv +9) 사용 시 Lua는 첫 Shiv 처리 후
ShivFirstDamageBonusUsed를 set(Attack 경로)하는데, JS 시뮬은 이 플래그
set이 else(비-Attack/Skill) 분기에 있어 Shiv(kind=Attack)는 도달 못 함
→ 플래그 영영 false → 모든 Shiv가 +9를 받아 시뮬이 데미지를 과대집계.

Lua가 정답(게임 정상) — 시뮬만 수정: 죽은 else-분기 플래그 set 제거 +
Attack 분기(baseDamage 계산 직후, Lua 순서와 동일)에 추가. RED-GREEN
테스트로 턴당 첫 Shiv만 보너스 검증. 87개.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:58:10 +09:00
72750f3647 fix(deck): Envenom attackPoison을 광역 공격에도 적용 (Lua 누락)
Envenom(독 바르기: 공격이 막히지 않은 피해를 줄 때마다 중독 1)이
단일타겟(DealDamageToTarget)에는 적용됐지만 광역(DealDamageToAllMonsters)
에는 빠져 있어, Envenom+광역공격이 게임에선 아무 적도 중독 안 됐다
(JS 미러는 양쪽 적용 — Lua가 누락).

DealDamageToAllMonsters의 막히지 않은 피해(dmg>0) 분기에 단일타겟과
동일한 attackPoison 적용을 추가(적별 ApplyPoisonToMonster). JS 미러는
이미 올바라 무변경. 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:53:24 +09:00
1291c52346 fix(balance): 시뮬 enemyStrengthLoss를 음수 힘 허용으로 (Lua 동기화)
PiercingWail(귀를 찢는 비명: 모든 적 힘 -6)에서 Lua는 적 공격을
(value+str-loss, 0클램프)로 줄여 StS처럼 힘이 음수로 작동하는데,
JS 시뮬은 max(0, str-loss)로 힘을 0에서 클램프해 모든 적 str=0일 때
공격이 전혀 안 줄었다(게임 -6, 시뮬 -0). 기존 테스트는 str>=loss
구간만 봐서 못 잡음.

Lua가 정답(게임은 정상) — 시뮬만 수정. calcEnemyAttack의 max(0,...)
제거(음수 힘 허용, 최종 calcAttack이 0클램프) + EnemyActStep을 그
헬퍼로 통일(중복 제거). RED-GREEN 테스트로 loss>str 구간 검증. 86개.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:50:22 +09:00
926733dbef fix(deck): firstCardDamageBonus 게이트 class→kind (영구 미발동 버그)
ChargedBlow(class=warrior, kind=Attack, firstCardDamageBonus=2)의 첫-카드
보너스가 Lua·JS 양쪽에서 `c.class == "Attack"`로 게이트돼 있었다. class는
warrior/bandit 등이라 절대 "Attack"이 아니어서 보너스가 영구 미발동(죽은 코드).
kind가 "Attack"이므로 `c.kind == "Attack"`로 수정(양쪽 미러).

RED-GREEN 회귀 테스트 추가: class=warrior·kind=Attack 카드의 첫 카드
보너스로 7뎀 → 1턴 처치(미수정 시 5뎀 2턴). 테스트 84→85.
산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:21:08 +09:00
d7813f9912 fix(deck): drawDamage/drawPoison를 per-play→per-draw로 (카드 설명과 일치)
Speedster("카드를 뽑을 때마다 피해")·CorrosiveWave("뽑을 때마다 중독")의
효과가 Lua에서는 ResolveCardEffects 끝에서 카드를 '낼 때마다' 발동해
카드 설명·JS 미러(sim-balance draw())와 어긋났다.

per-play 블록을 ApplyDrawTrigger() 메서드로 추출하고 DrawCards에서
뽑은 카드마다 호출해 per-draw로 정렬(JS와 동일). JS 미러는 이미
per-draw라 무변경 — 양쪽 일치.

부수: CheckCombatEnd에 self.CombatOver 멱등 가드 추가. per-draw로
호출이 잦아져(턴시작 5드로 등) 전멸 시 보상/골드/유물이 중복
발동할 수 있던 잠재 버그를 차단(공격+drawDamage 카드에서도 위험했음).

밸런스 영향: Speedster(Power)가 매턴 시작 드로에도 발동해 강해짐 —
값 튜닝은 sim으로 후속 조정 가능. 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:17:01 +09:00
e6f351420b fix(deck): BindButtons 1회 바인드 가드로 2회차 런 핸들러 중복 차단
StartRun이 run마다 BindButtons를 호출하는데 앞 7개 핸들러만
disconnect 가드돼 있고 reward/skip/map/shop/monster/relic/potion/job
등 ~30개 ConnectEvent는 미가드라, 2회차+ 런에서 핸들러가 누적된다.
특히 PickReward는 RewardChoices·CombatOver를 클리어하지 않아 중복
핸들러로 두 번 불리면 같은 보상 카드가 RunDeck에 2번 추가된다.

BindLobbyButtons/BindSoulShopButtons와 동일하게 self.ButtonsBound
1회 가드를 추가(런 UI 엔티티는 영속이라 1회 바인드로 충분).
신규 prop ButtonsBound 선언. 산출물 재생성 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
2026-06-29 18:02:49 +09:00