feat: Implement deck builder magnified card view, land advice, basic land integration, and unlimited time for deck construction.

This commit is contained in:
2025-12-17 16:15:20 +01:00
parent e5750d9729
commit e13aa16766
18 changed files with 672 additions and 264 deletions

View File

@@ -151,8 +151,20 @@ app.post('/api/packs/generate', async (req: Request, res: Response) => {
const { pools, sets } = packGeneratorService.processCards(poolCards, activeFilters);
// Extract available basic lands for deck building
const basicLands = pools.lands.filter(c => c.typeLine?.includes('Basic'));
// Deduplicate by Scryfall ID to get unique arts
const uniqueBasicLands: any[] = [];
const seenLandIds = new Set();
for (const land of basicLands) {
if (!seenLandIds.has(land.scryfallId)) {
seenLandIds.add(land.scryfallId);
uniqueBasicLands.push(land);
}
}
const packs = packGeneratorService.generatePacks(pools, sets, settings, numPacks || 108);
res.json(packs);
res.json({ packs, basicLands: uniqueBasicLands });
} catch (e: any) {
console.error("Generation error", e);
res.status(500).json({ error: e.message });
@@ -195,7 +207,10 @@ const draftInterval = setInterval(() => {
oracleId: card.oracle_id || card.id,
name: card.name,
imageUrl: card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal || "",
zone: 'library'
zone: 'library',
typeLine: card.typeLine || card.type_line || '',
oracleText: card.oracleText || card.oracle_text || '',
manaCost: card.manaCost || card.mana_cost || ''
});
});
}
@@ -213,8 +228,8 @@ io.on('connection', (socket) => {
// Timer management
// Timer management removed (Global loop handled)
socket.on('create_room', ({ hostId, hostName, packs }, callback) => {
const room = roomManager.createRoom(hostId, hostName, packs, socket.id); // Add socket.id
socket.on('create_room', ({ hostId, hostName, packs, basicLands }, callback) => {
const room = roomManager.createRoom(hostId, hostName, packs, basicLands || [], socket.id);
socket.join(room.id);
console.log(`Room created: ${room.id} by ${hostName}`);
callback({ success: true, room });
@@ -404,7 +419,10 @@ io.on('connection', (socket) => {
oracleId: card.oracle_id || card.id,
name: card.name,
imageUrl: card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal || "",
zone: 'library'
zone: 'library',
typeLine: card.typeLine || card.type_line || '',
oracleText: card.oracleText || card.oracle_text || '',
manaCost: card.manaCost || card.mana_cost || ''
});
});
}
@@ -428,7 +446,10 @@ io.on('connection', (socket) => {
oracleId: card.id,
name: card.name,
imageUrl: card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal || "",
zone: 'library'
zone: 'library',
typeLine: card.typeLine || card.type_line || '',
oracleText: card.oracleText || card.oracle_text || '',
manaCost: card.manaCost || card.mana_cost || ''
});
});
}
@@ -456,7 +477,10 @@ io.on('connection', (socket) => {
oracleId: card.oracle_id || card.id,
name: card.name,
imageUrl: card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal || "",
zone: 'library'
zone: 'library',
typeLine: card.typeLine || card.type_line || '',
oracleText: card.oracleText || card.oracle_text || '',
manaCost: card.manaCost || card.mana_cost || ''
});
});
});

View File

@@ -190,7 +190,8 @@ export class DraftManager extends EventEmitter {
}
} else if (draft.status === 'deck_building') {
// Check global deck building timer (e.g., 120 seconds)
const DECK_BUILDING_Duration = 120000;
// Disabling timeout as per request. Set to ~11.5 days.
const DECK_BUILDING_Duration = 999999999;
if (draft.startTime && (now > draft.startTime + DECK_BUILDING_Duration)) {
draft.status = 'complete'; // Signal that time is up
updates.push({ roomId, draft });

View File

@@ -12,6 +12,9 @@ interface CardInstance {
position: { x: number; y: number; z: number }; // For freeform placement
counters: { type: string; count: number }[];
ptModification: { power: number; toughness: number };
typeLine?: string;
oracleText?: string;
manaCost?: string;
}
interface PlayerState {

View File

@@ -21,6 +21,7 @@ interface Room {
hostId: string;
players: Player[];
packs: any[]; // Store generated packs (JSON)
basicLands?: any[];
status: 'waiting' | 'drafting' | 'deck_building' | 'playing' | 'finished';
messages: ChatMessage[];
maxPlayers: number;
@@ -29,13 +30,14 @@ interface Room {
export class RoomManager {
private rooms: Map<string, Room> = new Map();
createRoom(hostId: string, hostName: string, packs: any[], socketId?: string): Room {
createRoom(hostId: string, hostName: string, packs: any[], basicLands: any[] = [], socketId?: string): Room {
const roomId = Math.random().toString(36).substring(2, 8).toUpperCase();
const room: Room = {
id: roomId,
hostId,
players: [{ id: hostId, name: hostName, isHost: true, role: 'player', ready: false, socketId, isOffline: false }],
packs,
basicLands,
status: 'waiting',
messages: [],
maxPlayers: 8

View File

@@ -15,6 +15,8 @@ export interface DraftCard {
setCode: string;
setType: string;
finish?: 'foil' | 'normal';
oracleText?: string;
manaCost?: string;
[key: string]: any; // Allow extended props
}
@@ -102,6 +104,8 @@ export class PackGeneratorService {
setCode: cardData.set,
setType: setType,
finish: cardData.finish || 'normal',
oracleText: cardData.oracle_text || cardData.card_faces?.[0]?.oracle_text || '',
manaCost: cardData.mana_cost || cardData.card_faces?.[0]?.mana_cost || '',
};
// Add to pools