Initial commit

This commit is contained in:
2026-03-13 16:01:39 -04:00
commit 27e8c9a6fe
22 changed files with 8681 additions and 0 deletions

182
server/game/GameManager.js Normal file
View File

@@ -0,0 +1,182 @@
const { GameEngine } = require('../game/GameEngine');
const { buildStarterDeck, STARTER_DECKS, ALL_CARDS, CARD_DB } = require('../cards');
class GameManager {
constructor() {
this.rooms = new Map();
this.playerRooms = new Map();
this.games = new Map();
}
createRoom(hostId, hostName, roomName) {
const roomId = `room_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
const room = {
id: roomId,
name: roomName || `${hostName}'s Game`,
hostId,
players: [{ id: hostId, name: hostName, deck: null, ready: false }],
status: 'waiting',
gameId: null,
maxPlayers: 2,
};
this.rooms.set(roomId, room);
this.playerRooms.set(hostId, roomId);
return room;
}
joinRoom(roomId, playerId, playerName) {
const room = this.rooms.get(roomId);
if (!room) return { error: 'Room not found' };
if (room.status !== 'waiting') return { error: 'Game already in progress' };
if (room.players.length >= room.maxPlayers) return { error: 'Room is full' };
if (room.players.find((p) => p.id === playerId)) return { error: 'Already in room' };
room.players.push({ id: playerId, name: playerName, deck: null, ready: false });
this.playerRooms.set(playerId, roomId);
return { success: true, room };
}
leaveRoom(playerId) {
const roomId = this.playerRooms.get(playerId);
if (!roomId) return { error: 'Not in a room' };
const room = this.rooms.get(roomId);
if (!room) {
this.playerRooms.delete(playerId);
return { error: 'Room not found' };
}
room.players = room.players.filter((p) => p.id !== playerId);
this.playerRooms.delete(playerId);
if (room.players.length === 0) {
this.rooms.delete(roomId);
if (room.gameId) this.games.delete(room.gameId);
return { success: true, roomDeleted: true, roomId };
}
if (room.hostId === playerId) {
room.hostId = room.players[0].id;
}
return { success: true, room };
}
selectDeck(playerId, deckColor) {
const roomId = this.playerRooms.get(playerId);
if (!roomId) return { error: 'Not in a room' };
const room = this.rooms.get(roomId);
if (!room) return { error: 'Room not found' };
const player = room.players.find((p) => p.id === playerId);
if (!player) return { error: 'Player not in room' };
if (!STARTER_DECKS[deckColor]) return { error: 'Invalid deck color' };
player.deck = deckColor;
player.ready = true;
return { success: true, room };
}
selectCustomDeck(playerId, cardIds) {
const roomId = this.playerRooms.get(playerId);
if (!roomId) return { error: 'Not in a room' };
const room = this.rooms.get(roomId);
if (!room) return { error: 'Room not found' };
const player = room.players.find((p) => p.id === playerId);
if (!player) return { error: 'Player not in room' };
if (cardIds.length < 40) return { error: 'Deck must have at least 40 cards' };
if (cardIds.length > 60) return { error: 'Deck cannot exceed 60 cards' };
for (const cid of cardIds) {
if (!CARD_DB[cid]) return { error: `Invalid card id: ${cid}` };
}
const nonLandCounts = {};
for (const cid of cardIds) {
const card = CARD_DB[cid];
if (card.type !== 'land' || card.subtype !== 'basic') {
nonLandCounts[cid] = (nonLandCounts[cid] || 0) + 1;
if (nonLandCounts[cid] > 4) {
return { error: `Cannot have more than 4 copies of ${card.name}` };
}
}
}
player.deck = cardIds;
player.customDeck = true;
player.ready = true;
return { success: true, room };
}
startGame(roomId) {
const room = this.rooms.get(roomId);
if (!room) return { error: 'Room not found' };
if (room.players.length < 2) return { error: 'Need at least 2 players' };
if (!room.players.every((p) => p.ready)) return { error: 'Not all players are ready' };
const p1 = room.players[0];
const p2 = room.players[1];
const deck1 = p1.customDeck ? p1.deck : buildStarterDeck(p1.deck);
const deck2 = p2.customDeck ? p2.deck : buildStarterDeck(p2.deck);
const engine = new GameEngine(p1.id, p2.id, deck1, deck2);
const initialState = engine.startGame();
room.status = 'playing';
room.gameId = engine.id;
this.games.set(engine.id, engine);
return { success: true, gameId: engine.id, state: initialState };
}
getGame(gameId) {
return this.games.get(gameId);
}
handleGameAction(gameId, playerId, action) {
const engine = this.games.get(gameId);
if (!engine) return { error: 'Game not found' };
return engine.handleAction(playerId, action);
}
getGameState(gameId, playerId) {
const engine = this.games.get(gameId);
if (!engine) return null;
return engine.getState(playerId);
}
getRooms() {
const roomList = [];
for (const room of this.rooms.values()) {
roomList.push({
id: room.id,
name: room.name,
playerCount: room.players.length,
maxPlayers: room.maxPlayers,
status: room.status,
host: room.players.find((p) => p.id === room.hostId)?.name || 'Unknown',
});
}
return roomList;
}
getRoomDetails(roomId) {
return this.rooms.get(roomId) || null;
}
getCardList() {
return ALL_CARDS;
}
getStarterDecks() {
return STARTER_DECKS;
}
}
module.exports = { GameManager };