feat: initial Regnum project structure (Vue3 + Phaser3 + Socket.io)

This commit is contained in:
kevin.lhereec committed 2026-08-17 11:34:55 +02:00
commit 1db221e468
23 files changed
+1265

No files matched your search

+185
View File
@@ -0,0 +1,185 @@
const { dealHands, createDeck } = require('./cards')
const MAX_PLAYERS = 4
const MAX_TURNS = 10
const HAND_SIZE = 5
class Room {
constructor(id) {
this.id = id
this.players = [] // { socketId, name, score, hand, protected }
this.phase = 'lobby' // lobby | selecting | waiting | resolve | end
this.currentTurn = 0
this.maxTurns = MAX_TURNS
this.pendingPlays = new Map() // socketId -> card
this.deck = []
}
get playerCount() { return this.players.length }
addPlayer(socketId, name) {
if (this.players.length >= MAX_PLAYERS) return false
if (this.players.find(p => p.socketId === socketId)) return true
this.players.push({
socketId,
name,
score: 0,
hand: [],
protected: false
})
return true
}
removePlayer(socketId) {
this.players = this.players.filter(p => p.socketId !== socketId)
}
startGame() {
const { hands, remaining } = dealHands(this.players.length, HAND_SIZE)
this.deck = remaining
this.players.forEach((p, i) => { p.hand = hands[i] })
this.currentTurn = 1
this.phase = 'selecting'
this.pendingPlays.clear()
}
playCard(socketId, cardId) {
if (this.phase !== 'selecting') return { ok: false, error: 'Ce n\'est pas la phase de jeu' }
if (this.pendingPlays.has(socketId)) return { ok: false, error: 'Tu as déjà joué ce tour' }
const player = this.players.find(p => p.socketId === socketId)
if (!player) return { ok: false, error: 'Joueur introuvable' }
const cardIndex = player.hand.findIndex(c => c.id === cardId)
if (cardIndex === -1) return { ok: false, error: 'Carte non trouvée dans ta main' }
const card = player.hand.splice(cardIndex, 1)[0]
this.pendingPlays.set(socketId, { player, card })
this.phase = 'waiting'
return { ok: true }
}
allPlayersPlayed() {
return this.pendingPlays.size === this.players.length
}
resolveRound() {
const plays = [...this.pendingPlays.values()]
const playedCards = plays.map(p => ({ name: p.player.name, cardName: p.card.name }))
// Réinitialiser protection
this.players.forEach(p => { p.protected = false })
// 1. Appliquer les effets de protection (Enchanteur)
plays.forEach(({ player, card }) => {
if (card.effect === 'protect') {
player.protected = true
}
})
// 2. Calculer les points de base et effets
const messages = []
plays.forEach(({ player, card }) => {
// Points de base
player.score += card.points
if (!card.effect) return
const [effectType, valueStr] = card.effect.split(':')
const value = parseInt(valueStr) || 0
switch (effectType) {
case 'steal': {
// Voler des points au joueur le plus riche non protégé
const targets = this.players
.filter(p => p.socketId !== player.socketId && !p.protected && p.score > 0)
.sort((a, b) => b.score - a.score)
if (targets.length > 0) {
const stolen = Math.min(value, targets[0].score)
targets[0].score -= stolen
player.score += stolen
messages.push(`${player.name} vole ${stolen} pts à ${targets[0].name}`)
}
break
}
case 'bonus': {
// Bonus si le joueur a le plus de points de Royaume ce tour
const myRoyaume = plays
.filter(p => p.player.socketId === player.socketId && p.card.type === 'Royaume')
.reduce((sum, p) => sum + p.card.points, 0)
if (myRoyaume > 0) {
player.score += value
messages.push(`${player.name} gagne +${value} pts bonus (Commerce)`)
}
break
}
case 'double': {
// Double les points gagnés CE tour (uniquement la carte jouée)
player.score += card.points
messages.push(`${player.name} double ses gains (Alchimiste)`)
break
}
case 'draw': {
// Piocher une carte supplémentaire
if (this.deck.length > 0) {
player.hand.push(this.deck.pop())
messages.push(`${player.name} pioche une carte bonus`)
}
break
}
}
})
const message = messages.length > 0 ? messages.join(' | ') : 'Tour résolu !'
this.pendingPlays.clear()
this.phase = 'resolve'
return { playedCards, message }
}
nextTurn() {
this.currentTurn++
this.phase = 'selecting'
// Distribuer une nouvelle carte si la main est vide
this.players.forEach(p => {
if (p.hand.length === 0 && this.deck.length >= HAND_SIZE) {
p.hand = this.deck.splice(0, HAND_SIZE)
} else if (p.hand.length < 2 && this.deck.length > 0) {
p.hand.push(this.deck.pop())
}
})
}
isGameOver() {
return this.currentTurn >= this.maxTurns
}
getPlayersInfo() {
return this.players.map(p => ({
name: p.name,
score: p.score,
handSize: p.hand.length
}))
}
getStateFor(socketId) {
const player = this.players.find(p => p.socketId === socketId)
return {
players: this.getPlayersInfo(),
currentTurn: this.currentTurn,
maxTurns: this.maxTurns,
phase: this.phase,
hand: player ? player.hand : []
}
}
}
module.exports = { Room }