129 lines
3.6 KiB
JavaScript
129 lines
3.6 KiB
JavaScript
import React from 'react';
|
|
import CardArt from './CardArt';
|
|
|
|
const COLOR_MAP = {
|
|
radiance: { bg: '#fff8e7', border: '#d4a843', accent: '#f0d060' },
|
|
tide: { bg: '#e7f0ff', border: '#4477bb', accent: '#6699dd' },
|
|
shadow: { bg: '#2a2a2a', border: '#666', accent: '#8a7090', text: '#ddd' },
|
|
flame: { bg: '#fff0e7', border: '#cc4422', accent: '#ee6644' },
|
|
growth: { bg: '#eaf5e7', border: '#448833', accent: '#66aa55' },
|
|
colorless: { bg: '#f0f0f0', border: '#999', accent: '#bbb' },
|
|
};
|
|
|
|
const KEYWORD_DISPLAY = {
|
|
swift: 'Swift',
|
|
vigilant: 'Vigilant',
|
|
soaring: 'Soaring',
|
|
guardian: 'Guardian',
|
|
fortified: 'Fortified',
|
|
draining: 'Draining',
|
|
overwhelming: 'Overwhelming',
|
|
venomous: 'Venomous',
|
|
reaching: 'Reaching',
|
|
};
|
|
|
|
function formatManaCost(cost) {
|
|
if (!cost) return '';
|
|
const parts = [];
|
|
if (cost.colorless) parts.push(String(cost.colorless));
|
|
const symbols = { radiance: '\u2600', tide: '\u{1F30A}', shadow: '\u{1F480}', flame: '\u{1F525}', growth: '\u{1F33F}' };
|
|
for (const [color, sym] of Object.entries(symbols)) {
|
|
for (let i = 0; i < (cost[color] || 0); i++) parts.push(sym);
|
|
}
|
|
return parts.join(' ');
|
|
}
|
|
|
|
export default function Card({
|
|
card,
|
|
instance,
|
|
onClick,
|
|
selected,
|
|
selectable,
|
|
small,
|
|
faceDown,
|
|
zone,
|
|
}) {
|
|
if (faceDown) {
|
|
return (
|
|
<div className="card card-facedown" onClick={onClick}>
|
|
<div className="card-back">Arcane Duels</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const data = instance?.cardData || card;
|
|
if (!data) return null;
|
|
|
|
const colors = COLOR_MAP[data.color] || COLOR_MAP.colorless;
|
|
const isCreature = data.type === 'creature';
|
|
const isLand = data.type === 'land';
|
|
const tapped = instance?.tapped;
|
|
const keywords = data.keywords || [];
|
|
const power = instance ? instance.effectivePower : data.power;
|
|
const toughness = instance ? (instance.effectiveToughness - (instance.damage || 0)) : data.toughness;
|
|
|
|
const cardClass = [
|
|
'card',
|
|
`card-${data.type}`,
|
|
`color-${data.color}`,
|
|
tapped ? 'tapped' : '',
|
|
selected ? 'selected' : '',
|
|
selectable ? 'selectable' : '',
|
|
small ? 'card-small' : '',
|
|
zone ? `zone-${zone}` : '',
|
|
instance?.summoningSickness ? 'summoning-sick' : '',
|
|
].filter(Boolean).join(' ');
|
|
|
|
const style = {
|
|
'--card-bg': colors.bg,
|
|
'--card-border': colors.border,
|
|
'--card-accent': colors.accent,
|
|
color: colors.text || '#222',
|
|
};
|
|
|
|
return (
|
|
<div className={cardClass} style={style} onClick={onClick}>
|
|
<div className="card-header">
|
|
<span className="card-name">{data.name}</span>
|
|
{!isLand && <span className="card-cost">{formatManaCost(data.cost)}</span>}
|
|
</div>
|
|
|
|
<div className="card-art">
|
|
<CardArt
|
|
cardName={data.name}
|
|
color={data.color}
|
|
type={data.type}
|
|
width={small ? 100 : 130}
|
|
height={small ? 45 : 70}
|
|
/>
|
|
</div>
|
|
|
|
<div className="card-type-line">
|
|
{data.type.charAt(0).toUpperCase() + data.type.slice(1)}
|
|
{data.subtype ? ` \u2014 ${data.subtype}` : ''}
|
|
</div>
|
|
|
|
<div className="card-text">
|
|
{keywords.length > 0 && (
|
|
<div className="card-keywords">
|
|
{keywords.map((k) => KEYWORD_DISPLAY[k] || k).join(', ')}
|
|
</div>
|
|
)}
|
|
{data.flavor && !small && (
|
|
<div className="card-flavor">{data.flavor}</div>
|
|
)}
|
|
</div>
|
|
|
|
{isCreature && (
|
|
<div className="card-pt">
|
|
<span className={instance?.damage > 0 ? 'damaged' : ''}>
|
|
{power}/{toughness}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export { formatManaCost, COLOR_MAP };
|