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
This commit is contained in:
@@ -423,6 +423,9 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
const hitN = (c.hits || 1) + bonusHits;
|
const hitN = (c.hits || 1) + bonusHits;
|
||||||
let useAoe = c.aoe === true;
|
let useAoe = c.aoe === true;
|
||||||
if (c.class === 'shiv' && shivAoeThisCombat === true) useAoe = true;
|
if (c.class === 'shiv' && shivAoeThisCombat === true) useAoe = true;
|
||||||
|
if (c.class === 'shiv' && !shivFirstDamageBonusUsed && powerFieldTotal('firstShivDamageBonus') > 0) {
|
||||||
|
shivFirstDamageBonusUsed = true;
|
||||||
|
}
|
||||||
const perHit = calcAttack(baseDamage || 0, pStr, pWeak, 0) * turnAttackMultiplier;
|
const perHit = calcAttack(baseDamage || 0, pStr, pWeak, 0) * turnAttackMultiplier;
|
||||||
const dealToTarget = (target, amount) => {
|
const dealToTarget = (target, amount) => {
|
||||||
if (!target || !target.alive) return { killed: false, dealt: 0 };
|
if (!target || !target.alive) return { killed: false, dealt: 0 };
|
||||||
@@ -516,9 +519,6 @@ export function simulateCombat(data, rng, stats) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.class === 'shiv' && !shivFirstDamageBonusUsed && powerFieldTotal('firstShivDamageBonus') > 0) {
|
|
||||||
shivFirstDamageBonusUsed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.strength) pStr += c.strength;
|
if (c.strength) pStr += c.strength;
|
||||||
|
|||||||
@@ -902,6 +902,22 @@ test("calcEnemyAttack: 힘 손실이 base 아래로 공격을 낮춘다 (음수
|
|||||||
assert.equal(calcEnemyAttack(5, 0, 0, 0, 6), 0); // 5-6=-1 → 0 클램프
|
assert.equal(calcEnemyAttack(5, 0, 0, 0, 6), 0); // 5-6=-1 → 0 클램프
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('simulateCombat: firstShivDamageBonus는 턴당 첫 Shiv에만 적용 (Lua 동기화)', () => {
|
||||||
|
// PhantomBlades(firstShivDamageBonus 9) 활성. 턴당 3 Shiv 사용(에너지3·cost1).
|
||||||
|
// 정답(첫 Shiv만 +9): 턴1 = 10+1+1=12 → 13HP에 1 남김 → 2턴.
|
||||||
|
// 버그(모든 Shiv +9): 턴1 = 10*3=30 → 1턴.
|
||||||
|
const data = {
|
||||||
|
cards: {
|
||||||
|
PhantomBlades: { name: '환영검', cost: 0, kind: 'Power', firstShivDamageBonus: 9 },
|
||||||
|
Shiv: { name: '시브', cost: 1, kind: 'Attack', class: 'shiv', damage: 1 },
|
||||||
|
},
|
||||||
|
starterDeck: ['PhantomBlades', 'Shiv', 'Shiv', 'Shiv', 'Shiv'],
|
||||||
|
monsters: [{ name: '적', maxHp: 13, intents: [{ kind: 'Attack', value: 0 }] }],
|
||||||
|
};
|
||||||
|
const r = simulateCombat(data, mulberry32(1));
|
||||||
|
assert.equal(r.turns, 2);
|
||||||
|
});
|
||||||
|
|
||||||
test("simulateCombat: repeatOnKill repeats an attack until no kill occurs", () => {
|
test("simulateCombat: repeatOnKill repeats an attack until no kill occurs", () => {
|
||||||
const shared = {
|
const shared = {
|
||||||
cards: {
|
cards: {
|
||||||
|
|||||||
Reference in New Issue
Block a user