diff --git a/src/client/src/modules/draft/DeckBuilderView.tsx b/src/client/src/modules/draft/DeckBuilderView.tsx index 77c4ec2..f814a09 100644 --- a/src/client/src/modules/draft/DeckBuilderView.tsx +++ b/src/client/src/modules/draft/DeckBuilderView.tsx @@ -16,13 +16,21 @@ interface DeckBuilderViewProps { } // Internal Helper to normalize card data for visuals -const normalizeCard = (c: any): DraftCard => ({ - ...c, - finish: c.finish || 'nonfoil', - typeLine: c.typeLine || c.type_line, - // Ensure image is top-level for components that expect it - image: c.image || c.image_uris?.normal || c.card_faces?.[0]?.image_uris?.normal -}); +const normalizeCard = (c: any): DraftCard => { + const targetId = c.scryfallId || c.id; + const setCode = c.setCode || c.set; + const localImage = (targetId && setCode) + ? `/cards/images/${setCode}/full/${targetId}.jpg` + : null; + + return { + ...c, + finish: c.finish || 'nonfoil', + typeLine: c.typeLine || c.type_line, + // Ensure image is top-level for components that expect it + image: localImage || c.image || c.image_uris?.normal || c.card_faces?.[0]?.image_uris?.normal + }; +}; const LAND_URL_MAP: Record = { Plains: "https://cards.scryfall.io/normal/front/d/1/d1ea1858-ad25-4d13-9860-25c898b02c42.jpg", @@ -416,7 +424,21 @@ export const DeckBuilderView: React.FC = ({ initialPool, a }; const submitDeck = () => { - socketService.socket.emit('player_ready', { deck }); + // Normalize deck images to use local cache before submitting + const preparedDeck = deck.map(c => { + const targetId = c.scryfallId; // DraftCard uses scryfallId for the real ID + const setCode = c.setCode || c.set; + + if (targetId && setCode) { + return { + ...c, + image: `/cards/images/${setCode}/full/${targetId}.jpg` + }; + } + return c; + }); + + socketService.socket.emit('player_ready', { deck: preparedDeck }); }; // --- DnD Handlers --- @@ -526,13 +548,22 @@ export const DeckBuilderView: React.FC = ({ initialPool, a const landSourceCards = useMemo(() => { // If we have specific lands from cube, use them. if (availableBasicLands && availableBasicLands.length > 0) { - return availableBasicLands.map(land => ({ - ...land, - id: `land-source-${land.name}`, // stable ID for list - isLandSource: true, - // Ensure image is set for display - image: land.image || land.image_uris?.normal - })); + return availableBasicLands.map(land => { + const targetId = land.scryfallId || land.id; + const setCode = land.setCode || land.set; + + const localImage = (targetId && setCode) + ? `/cards/images/${setCode}/full/${targetId}.jpg` + : null; + + return { + ...land, + id: `land-source-${land.name}`, // stable ID for list + isLandSource: true, + // Ensure image is set for display + image: localImage || land.image || land.image_uris?.normal + }; + }); } // Otherwise generate generic basics diff --git a/src/client/src/modules/draft/DraftView.tsx b/src/client/src/modules/draft/DraftView.tsx index 34eb0b1..f74642e 100644 --- a/src/client/src/modules/draft/DraftView.tsx +++ b/src/client/src/modules/draft/DraftView.tsx @@ -9,11 +9,21 @@ import { DndContext, DragOverlay, useSensor, useSensors, MouseSensor, TouchSenso import { CSS } from '@dnd-kit/utilities'; // Helper to normalize card data for visuals -const normalizeCard = (c: any) => ({ - ...c, - finish: c.finish || 'nonfoil', - image: c.image || c.image_uris?.normal || c.card_faces?.[0]?.image_uris?.normal -}); +// Helper to normalize card data for visuals +const normalizeCard = (c: any) => { + const targetId = c.scryfallId || c.id; + const setCode = c.setCode || c.set; + + const localImage = (targetId && setCode) + ? `/cards/images/${setCode}/full/${targetId}.jpg` + : null; + + return { + ...c, + finish: c.finish || 'nonfoil', + image: localImage || c.image || c.image_uris?.normal || c.card_faces?.[0]?.image_uris?.normal + }; +}; // Droppable Wrapper for Pool const PoolDroppable = ({ children, className, style }: any) => { @@ -633,7 +643,8 @@ const DraftCardItem = ({ rawCard, handlePick, setHoveredCard }: any) => { ); }; -const PoolCardItem = ({ card, setHoveredCard, vertical = false }: any) => { +const PoolCardItem = ({ card: rawCard, setHoveredCard, vertical = false }: any) => { + const card = normalizeCard(rawCard); const { onTouchStart, onTouchEnd, onTouchMove, onClick } = useCardTouch(setHoveredCard, () => { if (window.matchMedia('(pointer: coarse)').matches) return; }, card); @@ -649,7 +660,7 @@ const PoolCardItem = ({ card, setHoveredCard, vertical = false }: any) => { onClick={onClick} > {card.name} = ({ card, onDragStart, return () => unregisterCard(card.instanceId); }, [card.instanceId]); + // Robustly resolve Art Crop // Robustly resolve Art Crop let imageSrc = card.imageUrl; - if (viewMode === 'cutout' && card.definition) { + + if (card.definition && card.definition.set && card.definition.id) { + if (viewMode === 'cutout') { + imageSrc = `/cards/images/${card.definition.set}/crop/${card.definition.id}.jpg`; + } else { + imageSrc = `/cards/images/${card.definition.set}/full/${card.definition.id}.jpg`; + } + } else if (viewMode === 'cutout' && card.definition) { if (card.definition.image_uris?.art_crop) { imageSrc = card.definition.image_uris.art_crop; } else if (card.definition.card_faces?.[0]?.image_uris?.art_crop) { diff --git a/src/client/src/modules/game/GameView.tsx b/src/client/src/modules/game/GameView.tsx index 9d7bc1b..855589c 100644 --- a/src/client/src/modules/game/GameView.tsx +++ b/src/client/src/modules/game/GameView.tsx @@ -501,7 +501,12 @@ export const GameView: React.FC = ({ gameState, currentPlayerId } > {hoveredCard && ( { + if (hoveredCard.definition?.set && hoveredCard.definition?.id) { + return `/cards/images/${hoveredCard.definition.set}/full/${hoveredCard.definition.id}.jpg`; + } + return hoveredCard.imageUrl; + })()} alt={hoveredCard.name} className="w-full h-full object-cover rounded-xl shadow-2xl shadow-black ring-1 ring-white/10" /> diff --git a/src/client/src/modules/game/ZoneOverlay.tsx b/src/client/src/modules/game/ZoneOverlay.tsx index af3e67f..0be3524 100644 --- a/src/client/src/modules/game/ZoneOverlay.tsx +++ b/src/client/src/modules/game/ZoneOverlay.tsx @@ -52,7 +52,12 @@ export const ZoneOverlay: React.FC = ({ zoneName, cards, onClo }} > { + if (card.definition?.set && card.definition?.id) { + return `/cards/images/${card.definition.set}/full/${card.definition.id}.jpg`; + } + return card.imageUrl || 'https://via.placeholder.com/250x350'; + })()} alt={card.name} className="w-full h-full object-cover" />