mafia / frontend /src /systems /CardManager.ts
Alfaxad's picture
Migrate Mafia game to ZeroGPU Gradio Space
c670567 verified
Raw
History Blame Contribute Delete
3.69 kB
/**
* ============================================================================
* CARD MANAGER - Deck & hand lifecycle management
* ============================================================================
*
* Manages the card game lifecycle: deck construction, shuffling,
* drawing, hand management, and discarding.
*
* This is a LOGIC-ONLY system. The Card UI component handles rendering.
*
* EVENTS (via Phaser.Events.EventEmitter):
* - 'cardDrawn': (card) => void
* - 'cardDiscarded': (card) => void
* - 'cardPlayed': (card) => void
* - 'handUpdated': (hand[]) => void
* - 'deckEmpty': () => void
* - 'deckShuffled': () => void
*
* USAGE:
* const cm = new CardManager(scene);
* cm.initDeck(cardConfigs);
* cm.shuffle();
* cm.drawToHand(3);
* const card = cm.getHand()[0];
* cm.playCard(card);
*/
import Phaser from 'phaser';
import { type CardConfig } from '../scenes/BaseBattleScene';
export class CardManager extends Phaser.Events.EventEmitter {
private scene: Phaser.Scene;
private deck: CardConfig[] = [];
private hand: CardConfig[] = [];
private discardPile: CardConfig[] = [];
private playedThisTurn: CardConfig[] = [];
private maxHandSize: number;
constructor(scene: Phaser.Scene, maxHandSize: number = 5) {
super();
this.scene = scene;
this.maxHandSize = maxHandSize;
}
// -- Deck Setup --
/** Initialize the deck with card configurations. */
initDeck(cards: CardConfig[]): void {
this.deck = [...cards];
}
/** Shuffle the deck. */
shuffle(): void {
Phaser.Utils.Array.Shuffle(this.deck);
this.emit('deckShuffled');
}
// -- Draw & Discard --
/** Draw cards from deck into hand (up to maxHandSize). */
drawToHand(count: number): CardConfig[] {
const drawn: CardConfig[] = [];
for (let i = 0; i < count && this.hand.length < this.maxHandSize; i++) {
if (this.deck.length === 0) {
this.reshuffleDiscard();
if (this.deck.length === 0) {
this.emit('deckEmpty');
break;
}
}
const card = this.deck.pop()!;
this.hand.push(card);
drawn.push(card);
this.emit('cardDrawn', card);
}
this.emit('handUpdated', [...this.hand]);
return drawn;
}
/** Play a card from hand (removes it). */
playCard(card: CardConfig): boolean {
const index = this.hand.findIndex((c) => c.id === card.id);
if (index === -1) return false;
this.hand.splice(index, 1);
this.playedThisTurn.push(card);
this.emit('cardPlayed', card);
this.emit('handUpdated', [...this.hand]);
return true;
}
/** Discard a card from hand. */
discardFromHand(card: CardConfig): boolean {
const index = this.hand.findIndex((c) => c.id === card.id);
if (index === -1) return false;
this.hand.splice(index, 1);
this.discardPile.push(card);
this.emit('cardDiscarded', card);
this.emit('handUpdated', [...this.hand]);
return true;
}
/** Discard entire hand. */
discardHand(): void {
this.discardPile.push(...this.hand);
this.hand = [];
this.emit('handUpdated', []);
}
/** End turn: move played cards to discard. */
endTurn(): void {
this.discardPile.push(...this.playedThisTurn);
this.playedThisTurn = [];
}
// -- Query --
getHand(): CardConfig[] {
return [...this.hand];
}
getDeckSize(): number {
return this.deck.length;
}
getDiscardSize(): number {
return this.discardPile.length;
}
// -- Internal --
/** Reshuffle discard pile back into deck. */
private reshuffleDiscard(): void {
this.deck = [...this.discardPile];
this.discardPile = [];
this.shuffle();
}
}