const PHASE_REINFORCE = 1; const PHASE_ATTACK = 2; const PHASE_FORTIFY = 3; class Player { constructor(id, name) { this.name = name; this.timeout = null; this.id = id; this.ready = false; // Data which is reset every turn this.isPlaying = false; this.reinforcementsPlaced = 0; this.reinforcementsAvailable = 0; this.turnPhase = PHASE_REINFORCE; this.resetColor(); } resetTimeout() { if (this.timeout !== null) { window.clearTimeout(this.timeout); } this.timeout = window.setTimeout(() => { if (players[this.id] !== undefined) { delete players[this.id]; } updateDom(); }, TIMEOUT); } /** * Get our color as used on the board. */ getColor() { return this.color; } resetColor() { let randomColor = Math.random() * 360; this.color = `hsl(${randomColor} 57% 50%)`; } /** * Claim a region of the map. * * @param data Data received via socket. */ claim(data) { let region = Region.getRegion(data.region); if (region.owner === null) { region.claim(this); return true; } else { return false; } } /** * Reinforce a region of the map. * * @param data Data received via socket. */ reinforce(data) { let region = Region.getRegion(data.region); if (region.owner === this) { region.reinforce(1); return true; } else { return false; } } /** * Process a generic action packet representing a player's move. * * @param data Data received via socket * @returns {boolean} Whether this player's turn has ended or not. */ act(data) { console.log(`player: ${this.id}`); console.log(data); if (this.turnPhase === PHASE_REINFORCE) { if (this.reinforce(data)) { this.reinforcementsPlaced += 1; } if (this.reinforcementsPlaced === this.reinforcementsAvailable) { this.turnPhase = PHASE_ATTACK; } return false; } else { // End turn prematurely. if (data.action === "END") { return true; } if (this.turnPhase === PHASE_ATTACK && data.action === "ATTACK") { if (this.attack(data)) { this.turnPhase = PHASE_FORTIFY; } return false; } this.turnPhase = PHASE_FORTIFY; if (data.action === "FORTIFY") { return this.fortify(data); } } return false; } /** * Process an action which is to attack another region. * * @param data Data received via socket */ attack(data) {} /** * Process an action which is to attack another region. * * @param data Data received via socket */ fortify(data) { let sender = Region.getRegion(data.startRegion); let receiver = Region.getRegion(data.endRegion); let strength = parseInt(data.strength); if ( sender.owner === this && receiver.owner === this && sender.strength > strength ) { receiver.reinforce(strength); sender.strength -= strength; return true; } else { return false; } } /** * Calculate the number of additional reinforcements to gain on this turn. */ additionalReinforcements() { return Math.min( 3, Math.floor( Object.values(REGIONS).filter((region) => region.owner === this).length / 3 ) ); } /** * Start a player's turn. */ startTurn() { this.isPlaying = true; this.reinforcementsPlaced = 0; this.reinforcementsAvailable = this.additionalReinforcements(); this.turnPhase = PHASE_REINFORCE; } /** * End player's turn */ endTurn() { this.isPlaying = false; this.nextPlayer().startTurn(); } nextPlayer() { let sorted = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1)); let ourIndex = sorted.findIndex((player) => player.id === this.id); return sorted[(ourIndex + 1) % sorted.length]; } }