"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var blackjack_exports = {}; __export(blackjack_exports, { Blackjack: () => Blackjack, commands: () => commands, roomSettings: () => roomSettings }); module.exports = __toCommonJS(blackjack_exports); var import_lib = require("../../lib"); /** * Blackjack Game * Pokemon Showdown - http://pokemonshowdown.com/ * * This allows users to play the classic blackjack card game. * Credits: jd, panpawn * * @license MIT license */ class Blackjack extends Rooms.RoomGame { constructor(room, user, autostartMinutes = 0) { super(room); this.gameNumber = room.nextGameNumber(); this.room = room; this.turnTimeoutMinutes = 1; this.timerTickSeconds = 5; this.createdBy = user.name; this.startedBy = ""; this.allowRenames = true; this.playerCap = 16; this.minimumPlayers = 2; this.playerScrollWheel = 4; this.cardWidth = 50; this.cardHeight = 85; this.spectators = /* @__PURE__ */ Object.create(null); this.dealer = new BlackjackDealer(); this.symbols = { "\u2665": "Hearts", "\u2666": "Diamonds", "\u2663": "Clubs", "\u2660": "Spades" }; this.deck = new BlackjackDeck().shuffle(); this.roomID = this.room.roomid; this.title = `Blackjack (${room.title})`; this.state = "signups"; this.lastMessage = ""; this.turnLog = ""; this.uhtmlChange = ""; this.curUsername = ""; this.endedBy = ""; this.infoboxLimited = ""; this.button = ' | '; this.spectateButton = ''; this.slideButton = ''; this.autostart = null; this.dqTimer = null; this.timerTick = null; this.makeGame(autostartMinutes); } /** * Game Setup * makeGame - configures required settings for creating a game * makePlayer - adds blackjack-specific properties to player object * sendInvite - called when a game is created, or when a player joins/leaves */ makeGame(autostartMinutes = 0) { if (autostartMinutes > 0) { this.autostart = setTimeout(() => this.start(), autostartMinutes * 6e4); } this.sendInvite(); } makePlayer(user) { return new BlackjackPlayer(user, this); } sendInvite() { const change = this.uhtmlChange; const players = Object.keys(this.playerTable); const playerList = []; for (const player of players) playerList.push(import_lib.Utils.escapeHTML(this.playerTable[player].name)); this.room.send(`|uhtml${change}|blackjack-${this.gameNumber}|
${this.createdBy} has created a game of Blackjack. ${this.button}
Players (${players.length}): ${!players.length ? "(None)" : playerList.join(", ")}
`); this.uhtmlChange = "change"; } /** * Joining/Leaving/Viewing * joinGame - joins the game * leaveGame - leaves the game * spectate - spectates the game * unspectate - stops spectating the game */ joinGame(user) { if (!user.named) return this.errorMessage(user, `You must first choose a name to play Blackjack.`); if (this.state === "started") return this.errorMessage(user, `Blackjack has already started.`); const joined = this.addPlayer(user); if (!joined) { this.errorMessage(user, `You are already in this game.`); return false; } this.sendInvite(); if (Object.keys(this.playerTable).length === this.playerCap) { this.start(); } if (this.spectators[user.id]) delete this.spectators[user.id]; return true; } leaveGame(user) { if (this.state === "started") return this.errorMessage(user, `You cannot leave this game; it has already started.`); if (!this.playerTable[user.id]) return this.errorMessage(user, "You are not in this game to leave."); this.removePlayer(user); this.sendInvite(); } spectate(user) { if (this.spectators[user.id]) return this.errorMessage(user, `You are already spectating this game.`); if (this.playerTable[user.id]) { return this.errorMessage(user, `You don't need to spectate the game; you're playing the game.`); } this.spectators[user.id] = user.id; user.sendTo(this.roomid, `You are now spectating this game.`); } unspectate(user) { if (!this.spectators[user.id]) return this.errorMessage(user, `You are already not spectating this game.`); delete this.spectators[user.id]; user.sendTo(this.roomid, `You are no longer spectating this game.`); } /** * Utility * errorMessage - sends a user an error message * add - adds/sends text to room * display - displays gameplay to players and spectators * clear - clears a user's gameplay screen * clearAllTimers - clears all possible existing timers pertaining to blackjack * slide - slides the game log down in the chat * onConnect - handles replies to send when a user joins the room, if any * onUpdateConnection - overrides default onUpdateConnection * createTimer - creates a timer with a countdown for a player * generateCard - generates the card for the UI * getWinners - returns an array of the winners and their cards */ errorMessage(user, message) { user.sendTo(this.room, import_lib.Utils.html`|html|
${message}
`); } send(message, clean = false) { const change = this.uhtmlChange; this.room.send(`|uhtml${change}|blackjack-${this.gameNumber}|
${clean ? message : this.lastMessage + message}
`); this.lastMessage += message; this.uhtmlChange = "change"; } display(text, clean = false, playerName, noChange = false, end = false) { const force = end && this.endedBy; let change = this.uhtmlChange; if (noChange) change = ""; if (clean) this.lastMessage = ""; const message = `|uhtml${change}|blackjack-${this.gameNumber}|
`; this.lastMessage += text; if (end) { text = `The game of blackjack has ${force ? `been forcibly ended by ${this.endedBy}` : "ended"}.
View turn log${this.turnLog}
${text}`; this.lastMessage = ""; } for (const player of Object.keys(this.playerTable)) { if (playerName && this.playerTable[player].name === playerName) { this.playerTable[player].gameLog += `${text}`; this.playerTable[player].sendRoom(`${message}${end ? text : this.playerTable[player].gameLog}
`); } else { this.playerTable[player].gameLog += text; this.playerTable[player].sendRoom(`${message}${end ? text : this.playerTable[player].gameLog}`); } } for (const spectatorID of Object.keys(this.spectators)) { const spectator = Users.get(this.spectators[spectatorID]); if (spectator) spectator.sendTo(this.roomid, `${message}${this.lastMessage + text}`); } } clear() { const player = this.playerTable[this.curUsername]; if (!player) throw new Error(`Player not in player table`); player.sendRoom(`|uhtmlchange|user-blackjack-${this.gameNumber}|`); } clearAllTimers() { if (this.dqTimer) { clearTimeout(this.dqTimer); this.dqTimer = null; } if (this.timerTick) { clearInterval(this.timerTick); this.timerTick = null; } if (this.autostart) { clearTimeout(this.autostart); this.autostart = null; } } slide(user) { user.sendTo(this.roomid, `|uhtml|blackjack-${this.gameNumber}|`); this.display("", false, user.name); } onConnect(user) { const message = `|uhtml|blackjack-${this.gameNumber}|
`; if (this.state === "signups") { this.sendInvite(); } else if (this.state === "started") { const player = this.playerTable[user.id]; const spectator = this.spectators[user.id]; if (player && user.id === toID(this.curUsername)) { player.sendRoom(`${message}${player.gameLog}`); player.sendRoom(player.playScreen.replace("|uhtmlchange|", "|uhtml|")); return; } else if (player) { player.sendRoom(`${message}${player.gameLog}`); return; } else if (spectator) { user.sendTo(this.roomid, `${message}${this.lastMessage}`); return; } } } onUpdateConnection() { } createTimer(user) { const player = this.playerTable[user.id]; this.dqTimer = setTimeout(() => { let cards = ""; for (const card of player.cards) cards += `[${card}] `; player.status = "stand"; this.display( import_lib.Utils.html`
${player.name} stands with ${cards}` + ` (${player.points}) (Auto-stand: took too long to move)`, false, this.playerTable[this.curUsername].name ); this.clear(); this.next(); }, this.turnTimeoutMinutes * 60 * 1e3); this.timerTick = setInterval(() => { const display = player.playScreen.replace("|uhtml|", "|uhtmlchange|"); if (display !== "") { const timeLeft = player.timerTicksLeft - 5; const buffer = String(timeLeft).length === 1 ? "0" : ""; const half = timeLeft <= this.turnTimeoutMinutes * 60 / 2; player.sendRoom(`${display} | [ 0:${buffer}${timeLeft}]`); player.timerTicksLeft -= this.timerTickSeconds; } }, this.timerTickSeconds * 1e3); } generateCard(card) { const value = toID(card).toUpperCase(); const symbolName = this.symbols[card.substr(-1)]; const red = ["D", "H"].includes(symbolName.charAt(0)); let cardUI = value; if (value === "K") cardUI = "King"; if (value === "Q") cardUI = "Queen"; if (value === "A") cardUI = "Ace"; if (value === "J") cardUI = "Joker"; return `
${value}
${card.substr(-1)}
${value}
${card.substr(-1)}
`; } getWinners(forceend = false) { const winners = []; if (forceend) this.giveCard("dealer"); if (this.dealer.points > 21) { for (const player of Object.keys(this.playerTable)) { if (this.playerTable[player].status === "bust") continue; winners.push( import_lib.Utils.html`${this.playerTable[player].name} ` + `[${this.playerTable[player].cards.join(", ")}]` ); } } else if (this.dealer.points !== 21) { for (const player of Object.keys(this.playerTable)) { if (this.playerTable[player].status === "bust" || this.playerTable[player].points <= this.dealer.points) continue; winners.push( import_lib.Utils.html`${this.playerTable[player].name} ` + `[${this.playerTable[player].cards.join(", ")}]` ); } } else if (this.dealer.points === 21) { winners.push(`${this.dealer.name} [${this.dealer.cards.join(", ")}]`); } return winners; } /** * Game State Changes * start - starts the game * end - ends the game * destroy - destroys the game */ start(user) { const numberOfPlayers = Object.keys(this.playerTable).length; if (numberOfPlayers < this.minimumPlayers) { if (this.autostart) { clearTimeout(this.autostart); this.autostart = null; } this.send("
Not enough players to start; game canceled."); this.destroy(); return; } if (user) this.startedBy = import_lib.Utils.escapeHTML(user.name); this.infoboxLimited = numberOfPlayers >= this.playerScrollWheel ? " infobox-limited" : ""; this.send(`[Blackjack has started. ${this.spectateButton}]`, true); this.curUsername = Object.keys(this.playerTable)[0]; const header = `The game of blackjack has started${this.startedBy !== "" ? ` (started by ${this.startedBy})` : ``}. ${this.slideButton}
`; this.state = "started"; this.giveCard("dealer"); this.giveCard("dealer"); this.turnLog += `${this.dealer.name}: [${this.dealer.cards[0]}]`; for (const player of Object.keys(this.playerTable)) { this.giveCard(this.playerTable[player].user.id); this.giveCard(this.playerTable[player].user.id); this.turnLog += import_lib.Utils.html`
${this.playerTable[player].name}: ` + `[${this.playerTable[player].cards[0]}] [${this.playerTable[player].cards[1]}] (${this.playerTable[player].points})`; } this.display(`${header}${this.turnLog}`, true, ""); this.next(); } end(user, cmd) { const force = cmd && toID(cmd) === "forceend"; if (user === true) { this.state = "ended"; this.destroy(); return true; } if (this.state === "started" && cmd && !force) { return this.errorMessage( user, `Because this game has started, you can only end this game by using /blackjack forceend.` ); } let winners = this.getWinners(); if (force) { winners = this.getWinners(true); this.endedBy = import_lib.Utils.escapeHTML(user.name); if (this.curUsername) { this.playerTable[this.curUsername].send(`|uhtmlchange|user-blackjack-${this.gameNumber}|`); } if (winners.length < 1) { this.display(`There are no winners this time.`, false, void 0, false, true); } else { this.display(`Winner${Chat.plural(winners.length)}: ${winners.join(", ")}`, false, void 0, false, true); } } if (!force && this.state !== "signups") { if (winners.length < 1) { this.display(`There are no winners this time.`, false, void 0, false, true); } else { this.display(`Winner${Chat.plural(winners.length)}: ${winners.join(", ")}`, false, void 0, false, true); } } else if (this.state === "signups") { this.send( import_lib.Utils.html`The game of blackjack has been ended by ${user.name}, ` + `and there are no winners because the game never started.`, true ); } this.state = "ended"; this.destroy(); return true; } destroy() { if (Object.keys(this.playerTable).length) { for (const player of Object.keys(this.playerTable)) { this.playerTable[player].destroy(); } } this.clearAllTimers(); this.room.game = null; } /** * Gameplay * hit - player decides to get a new card * stand - player decides to keep current cards * giveCard - gives a player a card from the deck * getCardPoints - returns the point value of a user's cards * next - game goes on to the next turn */ hit(user) { if (this.state !== "started") return this.errorMessage(user, `Blackjack hasn't started yet.`); if (!this.playerTable[user.id]) return this.errorMessage(user, `You aren't a player in this game.`); if (this.curUsername !== user.id) return this.errorMessage(user, `It's not your turn.`); this.playerTable[user.id].selfUhtml = "change"; this.playerTable[user.id].resetTimerTicks(); this.giveCard(user.id); } stand(user) { const player = this.playerTable[user.id]; if (this.state !== "started") return this.errorMessage(user, `Blackjack hasn't started yet.`); if (!player) return this.errorMessage(user, `You aren't a player in this game.`); if (this.curUsername !== user.id) return this.errorMessage(user, `It's not your turn.`); player.status = "stand"; let cards = ""; for (const card of player.cards) cards += `[${card}] `; const turnLine = import_lib.Utils.html`
${player.name} stands with ${cards} ` + `(${player.points}${player.points === 21 ? " - blackjack!" : ""})`; this.turnLog += turnLine; this.display(turnLine, false, player.name); this.clear(); this.next(); } giveCard(userid) { if (this.deck.length < 1) this.deck = new BlackjackDeck().shuffle(); const player = userid === "dealer" ? this.dealer : this.playerTable[userid]; if (!player) return; player.cards.push(this.deck[0]); this.deck.shift(); if (this.deck.length === 0) { this.display(`
${this.dealer.name} has ran out of cards in the deck; shuffling a new deck...`); this.deck = new BlackjackDeck().shuffle(); } player.points = this.getCardPoints(userid); if (player.cards.length < 3) return; let turnLine = import_lib.Utils.html`
${player.name} hit and received ` + `[${player.cards[player.cards.length - 1]}] (${player.points})`; this.turnLog += turnLine; if (player.cards.length > 2) this.display(turnLine, false, player.name); if (player instanceof BlackjackDealer) { if (player.points > 21) { let cards = ""; for (const card of player.cards) { cards += `[${card}] `; } turnLine = import_lib.Utils.html`
${this.dealer.name} has busted with ${cards} (${player.points})`; this.turnLog += turnLine; this.display(turnLine); this.end(true); return; } else if (player.points === 21) { turnLine = import_lib.Utils.html`
${this.dealer.name} has blackjack!`; this.turnLog += turnLine; this.display(turnLine); this.end(true); return; } } else if (player.points > 21) { player.status = "bust"; let cards = ""; for (const card of player.cards) { cards += `[${card}] `; } turnLine = import_lib.Utils.html`
${player.name} has busted with ${cards} (${player.points})`; this.turnLog += turnLine; this.display(turnLine, false, player.name); this.clear(); } else if (player.points === 21) { player.status = "stand"; turnLine = import_lib.Utils.html`
${player.name} has blackjack!`; this.turnLog += turnLine; this.display(turnLine, false, player.name); this.clear(); } if (player !== this.dealer) this.playerTable[userid].cards = player.cards; this.next(); } getCardPoints(playerid) { const player = playerid === "dealer" ? this.dealer : this.playerTable[playerid]; let points = 0; let aceCount = 0; for (const card of player.cards.map((x) => toID(x).toUpperCase())) { if (!isNaN(Number(card))) { points += Number(card); } else if (["K", "Q", "J"].includes(card)) { points += 10; } else if (card === "A") { points += 11; aceCount++; } } while (points > 21 && aceCount > 0) { points -= 10; aceCount--; } return points; } next() { this.clearAllTimers(); if (Object.keys(this.playerTable)[Object.keys(this.playerTable).length - 1] === this.curUsername && this.playerTable[this.curUsername].status !== "playing") { if (this.dealer.points < 17) { this.giveCard("dealer"); } else if (this.dealer.points >= 17) { let cards = ""; for (const card of this.dealer.cards) { cards += `[${card}] `; } const turnLine = `
${this.dealer.name} stands with ${cards} (${this.dealer.points})`; this.turnLog += turnLine; this.display(turnLine); this.end(true); } return; } if (this.playerTable[this.curUsername].status !== "playing") { let num = 0; while (this.playerTable[Object.keys(this.playerTable)[num]].status !== "playing") { num++; } this.curUsername = Object.keys(this.playerTable)[num]; } let output = `|uhtml${this.playerTable[this.curUsername].selfUhtml}|user-blackjack-${this.gameNumber}|
`; output += `It's your turn to move, ${this.playerTable[this.curUsername].name}
`; for (const card of this.playerTable[this.curUsername].cards) { output += this.generateCard(card); } output += `
Score: ${this.playerTable[this.curUsername].points}${this.playerTable[this.curUsername].points === 21 ? ` (you have blackjack!)` : ``}`; output += `
| `; this.playerTable[this.curUsername].sendRoom(`|notify|Blackjack (${this.room.title})|It's your turn to play!`); this.playerTable[this.curUsername].sendRoom(output); this.playerTable[this.curUsername].playScreen = output; this.createTimer(this.playerTable[this.curUsername].user); } } class BlackjackPlayer extends Rooms.RoomGamePlayer { constructor(user, game) { super(user, game); this.user = user; this.cards = []; this.points = 0; this.slide = 0; this.status = "playing"; this.selfUhtml = ""; this.gameLog = ""; this.playScreen = ""; this.timerTicksLeft = this.game.turnTimeoutMinutes * 60; } resetTimerTicks() { this.timerTicksLeft = this.game.turnTimeoutMinutes * 60; } } class BlackjackDealer { constructor() { this.cards = []; this.points = 0; this.name = "The Dealer"; } } class BlackjackDeck { constructor() { this.deck = [ "A\u2665", "A\u2666", "A\u2663", "A\u2660", "2\u2665", "2\u2666", "2\u2663", "2\u2660", "3\u2665", "3\u2666", "3\u2663", "3\u2660", "4\u2665", "4\u2666", "4\u2663", "4\u2660", "5\u2665", "5\u2666", "5\u2663", "5\u2660", "6\u2665", "6\u2666", "6\u2663", "6\u2660", "7\u2665", "7\u2666", "7\u2663", "7\u2660", "8\u2665", "8\u2666", "8\u2663", "8\u2660", "9\u2665", "9\u2666", "9\u2663", "9\u2660", "10\u2665", "10\u2666", "10\u2663", "10\u2660", "J\u2665", "J\u2666", "J\u2663", "J\u2660", "Q\u2665", "Q\u2666", "Q\u2663", "Q\u2660", "K\u2665", "K\u2666", "K\u2663", "K\u2660" ]; } shuffle() { return import_lib.Utils.shuffle(this.deck); } } const commands = { blj: "blackjack", blackjack: { new: "create", create(target, room, user) { room = this.requireRoom(); this.checkCan("minigame", null, room); if (room.game) return this.errorReply("There is already a game running in this room."); if (room.settings.blackjackDisabled) return this.errorReply("Blackjack is currently disabled in this room."); const autostartMinutes = target ? parseFloat(target) : 0; if (isNaN(autostartMinutes) || autostartMinutes <= 0 || autostartMinutes * 60 * 1e3 > Chat.MAX_TIMEOUT_DURATION) { return this.errorReply("Usage: /blackjack create [autostart] - where autostart is an integer"); } this.privateModAction(`A game of blackjack was created by ${user.name}.`); this.modlog(`BLACKJACK CREATE`); room.game = new Blackjack(room, user, autostartMinutes); }, start(target, room, user) { room = this.requireRoom(); this.checkCan("minigame", null, room); const game = this.requireGame(Blackjack); if (game.state !== "signups") return this.errorReply("This game of blackjack has already started."); this.privateModAction(`The game of blackjack was started by ${user.name}.`); this.modlog(`BLACKJACK START`); game.start(user); }, forceend: "end", end(target, room, user, connection, cmd) { room = this.requireRoom(); this.checkCan("minigame", null, room); const game = this.requireGame(Blackjack); const force = cmd === "forceend" ? "forcibly " : ""; const end = game.end(user, cmd); if (end) { this.privateModAction(`The game of blackjack was ${force}ended by ${user.name}.`); this.modlog(`BLACKJACK END`); } }, hit(target, room, user) { room = this.requireRoom(); const game = this.requireGame(Blackjack); game.hit(user); }, stand(target, room, user) { room = this.requireRoom(); const game = this.requireGame(Blackjack); game.stand(user); }, slide(target, room, user) { room = this.requireRoom(); const game = this.requireGame(Blackjack); game.slide(user); }, j: "join", join(target, room, user) { return this.parse("/joingame"); }, l: "leave", leave(target, room, user) { return this.parse("/leavegame"); }, unspectate: "spectate", spectate(target, room, user, connection, cmd) { room = this.requireRoom(); const game = this.requireGame(Blackjack); if (cmd === "spectate") { game.spectate(user); } else if (cmd === "unspectate") { game.unspectate(user); } }, disable(target, room, user) { room = this.requireRoom(); this.checkCan("gamemanagement", null, room); if (room.settings.blackjackDisabled) { return this.errorReply("Blackjack is already disabled in this room."); } room.settings.blackjackDisabled = true; room.saveSettings(); this.sendReply(`Blackjack has been disabled for this room.`); }, enable(target, room, user) { room = this.requireRoom(); this.checkCan("gamemanagement", null, room); if (!room.settings.blackjackDisabled) { return this.errorReply("Blackjack is already enabled in this room."); } delete room.settings.blackjackDisabled; room.saveSettings(); this.sendReply(`Blackjack has been enabled for this room.`); }, "": "help", help(target, room, user) { return this.parse("/help blackjack"); } }, blackjackhelp: [ "/blackjack create - Creates a game of blackjack. Requires: % @ # &", "/blackjack create [autostart] - Automatically creates a game of blackjack in [autostart] minutes. Requires: % @ # &", "/blackjack start - Starts a game of blackjack. Requires: % @ # &", "/blackjack end - Ends a game of blackjack. Requires: % @ # &", "/blackjack join - Joins a game of blackjack.", "/blackjack leave - Leaves a game of blackjack.", "/blackjack spectate - Spectates a game of blackjack.", "/blackjack unspectate - Stops spectating a game of blackjack.", "/blackjack disable - Prevents games of blackjack from being made in the room. Requires: # &", "/blackjack enable - Allows games of blackjack to be made in the room. Requires: # &" ] }; const roomSettings = (room) => ({ label: "Blackjack", permission: "editroom", options: [ [`disabled`, room.settings.blackjackDisabled || "blackjack disable"], [`enabled`, !room.settings.blackjackDisabled || "blackjack enable"] ] }); //# sourceMappingURL=blackjack.js.map