Files
ProjectSS/client/src/core/components/CombinePanel.jsx
T
kyoung5seo 83060f3e5b 구현 우선 순위 1차 진행 완료
- 1차 — 코어 검증 (여기까지만 되어도 재미 판정 가능)
  1. **기호 체계 + 책상 상태 상시 노출 UI** (§3)
  2. **책상 적재 + 누진 패시브 페널티 A·B** (§4)
  3. **표준 족보 판정 및 합본 실행** (§5)
  4. **합본 기반 토큰 산출** (§8.2)

 게임 플레이 후 피드백 해결
 - 내용은 아래 문서 참고
 - docs/decisions/2026-09-05_core-graybox-ux-feedback.md
2026-09-05 01:06:41 +09:00

70 lines
2.9 KiB
React

import React, { useMemo, useState } from 'react';
import SymbolBadge from './SymbolBadge';
import { detectHands, resolveCombine, HAND_DEFS } from '../engine/hands';
import './CombinePanel.css';
// 합본 결재 (§5) — 책상에서 카드를 다중 선택하면 성립 가능한 족보를 자동 판정해 보여준다.
// 여러 족보가 동시 성립하면 최고 족보를 기본값으로 보여주되, 하위 족보도 선택할 수 있게 한다 (§5.3) —
// 명분 종류를 고르기 위해 일부러 하위 족보를 쓰는 선택이 존재해야 하기 때문이다.
export default function CombinePanel({ deskCards, onConfirm, onClose }) {
const [selectedIds, setSelectedIds] = useState([]);
const selection = useMemo(
() => deskCards.filter((c) => selectedIds.includes(c.id)),
[deskCards, selectedIds]
);
const matches = useMemo(() => detectHands(selection), [selection]);
const toggle = (id) => {
setSelectedIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
};
const confirm = (patternId) => {
const result = resolveCombine(selection, patternId);
if (result) onConfirm(result);
};
return (
<div className="combine-overlay" onClick={onClose}>
<div className="combine-panel" onClick={(e) => e.stopPropagation()}>
<div className="combine-header">
<h2>합본 결재</h2>
<button className="combine-close" onClick={onClose}>&times;</button>
</div>
<p className="combine-hint">책상에서 카드를 선택하면 성립하는 족보를 판정합니다.</p>
<div className="combine-desk-grid">
{deskCards.length === 0 && <p className="combine-empty">책상이 비어 있습니다.</p>}
{deskCards.map((c) => (
<button
key={c.id}
className={`combine-card ${selectedIds.includes(c.id) ? 'selected' : ''}`}
onClick={() => toggle(c.id)}
>
<SymbolBadge axis={c.axis} grade={c.grade} />
{c.name && <span className="combine-card-name">{c.name}</span>}
</button>
))}
</div>
<div className="combine-results">
{selection.length > 0 && matches.length === 0 && (
<p className="combine-no-match">선택한 {selection.length}장은 성립하는 족보가 없습니다.</p>
)}
{matches.map((patternId) => {
const def = HAND_DEFS[patternId];
return (
<button key={patternId} className="combine-match" onClick={() => confirm(patternId)}>
<span className="match-name">{def.name}</span>
<span className="match-score">실적 +{def.score}</span>
<span className="match-justification">명분 +{def.baseJustification}</span>
</button>
);
})}
</div>
</div>
</div>
);
}