feat: Implement resume match button, enhance card image loading, and improve server-side logging and deck handling.
This commit is contained in:
@@ -82,7 +82,7 @@ define(['./workbox-5a5d9309'], (function (workbox) { 'use strict';
|
|||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.ca9afac9bpo"
|
"revision": "0.99c1h4jant"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|||||||
@@ -61,10 +61,14 @@ export const CardVisual: React.FC<CardVisualProps> = ({
|
|||||||
// Robustly resolve Image Source based on viewMode
|
// Robustly resolve Image Source based on viewMode
|
||||||
let src = card.imageUrl || card.image;
|
let src = card.imageUrl || card.image;
|
||||||
|
|
||||||
|
// Use top-level properties if available (common in DraftCard / Game Card objects)
|
||||||
|
const setCode = card.setCode || card.set || card.definition?.set;
|
||||||
|
const cardId = card.scryfallId || card.definition?.id;
|
||||||
|
|
||||||
if (viewMode === 'cutout') {
|
if (viewMode === 'cutout') {
|
||||||
// Priority 1: Local Cache (standard naming convention) - PREFERRED BY USER
|
// Priority 1: Local Cache (standard naming convention) - PREFERRED BY USER
|
||||||
if (card.definition?.set && card.definition?.id) {
|
if (setCode && cardId) {
|
||||||
src = `/cards/images/${card.definition.set}/crop/${card.definition.id}.jpg`;
|
src = `/cards/images/${setCode}/crop/${cardId}.jpg`;
|
||||||
}
|
}
|
||||||
// Priority 2: Direct Image URIs (if available) - Fallback
|
// Priority 2: Direct Image URIs (if available) - Fallback
|
||||||
else if (card.image_uris?.art_crop || card.image_uris?.crop) {
|
else if (card.image_uris?.art_crop || card.image_uris?.crop) {
|
||||||
@@ -77,15 +81,19 @@ export const CardVisual: React.FC<CardVisualProps> = ({
|
|||||||
else if (card.definition?.card_faces?.[0]?.image_uris?.art_crop) {
|
else if (card.definition?.card_faces?.[0]?.image_uris?.art_crop) {
|
||||||
src = card.definition.card_faces[0].image_uris.art_crop;
|
src = card.definition.card_faces[0].image_uris.art_crop;
|
||||||
}
|
}
|
||||||
// Priority 4: If card has a manually set image property that looks like a crop (less reliable)
|
// Priority 4: Server-provided explicit property
|
||||||
|
else if (card.imageArtCrop) {
|
||||||
|
src = card.imageArtCrop;
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback: If no crop found, src remains whatever it was (likely full)
|
// Fallback: If no crop found, src remains whatever it was (likely full)
|
||||||
} else {
|
} else {
|
||||||
// Normal / Full View
|
// Normal / Full View
|
||||||
|
|
||||||
// Priority 1: Local Cache (standard naming convention) - PREFERRED
|
// Priority 1: Local Cache (standard naming convention) - PREFERRED
|
||||||
if (card.definition?.set && card.definition?.id) {
|
if (setCode && cardId) {
|
||||||
// Check if we want standard full image path
|
// Check if we want standard full image path
|
||||||
src = `/cards/images/${card.definition.set}/full/${card.definition.id}.jpg`;
|
src = `/cards/images/${setCode}/full/${cardId}.jpg`;
|
||||||
}
|
}
|
||||||
// Priority 2: Direct Image URIs
|
// Priority 2: Direct Image URIs
|
||||||
else if (card.image_uris?.normal) {
|
else if (card.image_uris?.normal) {
|
||||||
|
|||||||
@@ -107,6 +107,16 @@ export const TournamentManager: React.FC<TournamentManagerProps> = ({ tournament
|
|||||||
<Play className="w-4 h-4" /> Play Match
|
<Play className="w-4 h-4" /> Play Match
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Resume Button */}
|
||||||
|
{match.status === 'in_progress' && isMyMatch && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleJoinMatch(match.id)}
|
||||||
|
className="w-full bg-emerald-600 hover:bg-emerald-500 text-white font-bold py-2 flex items-center justify-center gap-2 transition-colors animate-pulse"
|
||||||
|
>
|
||||||
|
<Play className="w-4 h-4" /> Resume Match
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -615,7 +615,8 @@ export class RulesEngine {
|
|||||||
console.log(`Player ${playerId} draws ${card.name}`);
|
console.log(`Player ${playerId} draws ${card.name}`);
|
||||||
} else {
|
} else {
|
||||||
// Empty library loss?
|
// Empty library loss?
|
||||||
console.log(`Player ${playerId} attempts to draw from empty library.`);
|
// Empty library loss?
|
||||||
|
console.warn(`[RulesEngine] Player ${playerId} attempts to draw from empty library. (Deck size: 0)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export class GameManager extends EventEmitter {
|
|||||||
|
|
||||||
// Track rooms where a bot is currently "thinking" to avoid double-queuing
|
// Track rooms where a bot is currently "thinking" to avoid double-queuing
|
||||||
private thinkingRooms: Set<string> = new Set();
|
private thinkingRooms: Set<string> = new Set();
|
||||||
|
// Throttle logs
|
||||||
|
private lastBotLog: Record<string, number> = {};
|
||||||
|
|
||||||
// Helper to trigger bot actions if game is stuck or just started
|
// Helper to trigger bot actions if game is stuck or just started
|
||||||
public triggerBotCheck(roomId: string): StrictGameState | null {
|
public triggerBotCheck(roomId: string): StrictGameState | null {
|
||||||
@@ -86,7 +88,11 @@ export class GameManager extends EventEmitter {
|
|||||||
|
|
||||||
// If it is a Bot's turn to have priority, and we aren't already processing
|
// If it is a Bot's turn to have priority, and we aren't already processing
|
||||||
if (priorityPlayer?.isBot && !this.thinkingRooms.has(roomId)) {
|
if (priorityPlayer?.isBot && !this.thinkingRooms.has(roomId)) {
|
||||||
console.log(`[Bot Loop] Bot ${priorityPlayer.name} is thinking...`);
|
const now = Date.now();
|
||||||
|
if (!this.lastBotLog[roomId] || now - this.lastBotLog[roomId] > 5000) {
|
||||||
|
console.log(`[Bot Loop] Bot ${priorityPlayer.name} is thinking...`);
|
||||||
|
this.lastBotLog[roomId] = now;
|
||||||
|
}
|
||||||
this.thinkingRooms.add(roomId);
|
this.thinkingRooms.add(roomId);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -206,8 +212,18 @@ export class GameManager extends EventEmitter {
|
|||||||
|
|
||||||
if (!bot || !bot.isBot) return;
|
if (!bot || !bot.isBot) return;
|
||||||
|
|
||||||
// 1. Mulligan: Always Keep
|
// 1. Mulligan: Always Keep (but check if we have cards?)
|
||||||
if (game.step === 'mulligan') {
|
if (game.step === 'mulligan') {
|
||||||
|
const hand = Object.values(game.cards).filter(c => c.ownerId === botId && c.zone === 'hand');
|
||||||
|
if (hand.length === 0 && !bot.handKept) {
|
||||||
|
// We have NO cards to keep? Something is wrong (deck didn't load?).
|
||||||
|
// Don't loop infinitely trying to keep an empty hand if that's invalid,
|
||||||
|
// but technically "keeping" 0 cards is just accepting 0 cards.
|
||||||
|
// However, usually this means initialization failed.
|
||||||
|
// We'll log once and stop? Or just keep to unstuck the game?
|
||||||
|
// Let's try to keep.
|
||||||
|
// console.warn(`[Bot AI] ${bot.name} has 0 cards in hand during Mulligan. Initializing?`);
|
||||||
|
}
|
||||||
if (!bot.handKept) {
|
if (!bot.handKept) {
|
||||||
try { engine.resolveMulligan(botId, true, []); } catch (e) { }
|
try { engine.resolveMulligan(botId, true, []); } catch (e) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,12 @@ export class TournamentManager extends EventEmitter {
|
|||||||
// Update Player Deck in Tournament Roster
|
// Update Player Deck in Tournament Roster
|
||||||
const player = t.players.find(p => p.id === playerId);
|
const player = t.players.find(p => p.id === playerId);
|
||||||
if (player) {
|
if (player) {
|
||||||
player.deck = deck;
|
if (deck && deck.length > 0) {
|
||||||
|
player.deck = deck;
|
||||||
|
} else if (!player.deck || player.deck.length === 0) {
|
||||||
|
// Only warn if we are missing a deck entirely
|
||||||
|
console.warn(`[Tournament] received empty deck for ${playerId}, and no previous deck found.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to Ready
|
// Add to Ready
|
||||||
|
|||||||
Reference in New Issue
Block a user