이번 세션에서 발견·수정한 하네스 학습을 저장소(공유 매개)에 반영해 협업자(codex 등)도 적용받게 한다. 메모리는 로컬이라 공유 안 됨. - RULES.md §9 신설 (카드 kind ↔ 효과 일치): ResolveCardDrop 라우팅 (Attack=몬스터드롭/Skill·Power=스윕/Status=unplayable)·Power 분기가 damage/aoe 무시 → 데미지=Attack, block/유틸=Skill, 지속효과=Power. 안 맞으면 사용불가/死카드(아이언 바디·분노 사고). - tools/verify/cardkinds.mjs 신설: kind↔효과 위반(Attack-무데미지/ Power-무효과/미지원 kind) 정적 검출(이상 0=exit 0). 현재 main 147장 0, Defend=Attack·Rage=Power 위반은 2건 검출 확인. - docs/codex-working-rules.md 6~9 추가: ⑥ main 머지 충돌 시 머지 전체 revert 금지(소스 충돌만 해소·산출물 재생성 — #98/#99가 #96 날린 사고) ⑦ 카드 kind 일치+cardkinds 검증 ⑧ 변경 후 검증 스위트 ⑨ RULES.md 권위. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UUvHKjrt8jqLzDeCsRRGmj
43 lines
2.6 KiB
JavaScript
43 lines
2.6 KiB
JavaScript
// 카드 kind ↔ 효과 정합성 정적 검사 (협업자/codex가 카드 추가 후 실행).
|
|
// 배경(2026-06-30): kind가 효과와 안 맞으면 카드가 사용불가/死카드가 된다.
|
|
// - ResolveCardDrop 라우팅: Attack=몬스터 위 드롭(FindMonsterAtTouch>0 필요) / Skill·Power=위로 스윕 / Status=unplayable.
|
|
// → block·유틸만 있고 데미지 없는 카드를 Attack으로 두면 위로 스윕으로 못 쓴다(아이언 바디 사고).
|
|
// - PlayCard의 Power 분기는 PlayerPowers 등록만 하고 damage/aoe를 무시한다.
|
|
// → Power인데 powerEffect도 power필드도 없으면 재생 시 아무 효과 없는 死카드(분노 사고).
|
|
// 사용: node tools/verify/cardkinds.mjs (이상 0 → exit 0, 있으면 목록 + exit 1)
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
const cards = JSON.parse(readFileSync('data/cards.json', 'utf8')).cards;
|
|
|
|
// Power 카드를 실제로 기능하게 하는 필드(powerEffect 지속효과 + 온플레이/지속 power 필드).
|
|
// damage/aoe/block 같은 Attack/Skill 전용 필드는 Power 분기서 무시되므로 제외.
|
|
const POWER_FIELDS = [
|
|
'powerEffect', 'strength', 'dex', 'thorns', 'intangible',
|
|
'turnStartShiv', 'turnStartDraw', 'turnStartDiscard',
|
|
'shivDamageBonus', 'firstShivDamageBonus', 'shivRetain', 'shivAoe',
|
|
'attackPoison', 'drawDamage', 'drawPoison', 'attackDamageVsWeakMultiplier',
|
|
'cardPlayedBlock', 'cardPlayedDamage', 'cardPlayedRandomDamage',
|
|
'extraPoisonTicks', 'poisonApplicationBurstEvery', 'poisonApplicationBurstDamage',
|
|
'skillSlyOnPlay', 'endTurnDexLoss',
|
|
];
|
|
const VALID_KINDS = ['Attack', 'Skill', 'Power', 'Status'];
|
|
|
|
const issues = [];
|
|
for (const [id, c] of Object.entries(cards)) {
|
|
if (!VALID_KINDS.includes(c.kind)) {
|
|
issues.push(`${id}(${c.name}): 미지원 kind="${c.kind}"`);
|
|
continue;
|
|
}
|
|
if (c.kind === 'Attack' && c.damage == null && c.xDamagePerEnergy == null) {
|
|
issues.push(`${id}(${c.name}): kind=Attack인데 damage 없음 → 몬스터 드롭 라우팅 불가(방어/유틸이면 kind=Skill)`);
|
|
}
|
|
if (c.kind === 'Power' && !POWER_FIELDS.some((f) => c[f] != null)) {
|
|
issues.push(`${id}(${c.name}): kind=Power인데 power효과 없음(死카드) → damage/aoe는 Power 분기서 무시, kind 재검토`);
|
|
}
|
|
}
|
|
|
|
console.log(`카드 ${Object.keys(cards).length}장 kind↔효과 정합성: 이상 ${issues.length}`);
|
|
for (const i of issues) console.log(' ⚠️ ' + i);
|
|
console.log(issues.length ? 'RESULT: 정합성 위반 (위 카드 kind 수정 필요)' : 'RESULT: 모든 카드 kind↔효과 일치 ✓');
|
|
process.exit(issues.length ? 1 : 0);
|