UNPKG

xoppack

Version:

A Easy Fun Game Package For Discord.js Developers To Use!

188 lines (187 loc) 13.5 kB
const { MessageEmbed } = require('discord.js') const { MessageButton, MessageActionRow } = require('discord-buttons') const lineReplyNoMention = require('discord-reply'); const reactions = { a1: 1, a2: 2, a3: 3, b1: 4, b2: 5, b3: 6, c1: 7, c2: 8, c3: 9 }; const NO_MOVE = 0; const PLAYER_1 = 1; const PLAYER_2 = 2; class TicTacToe { constructor(options = {}) { if (!options.message) throw new TypeError('NO_MESSAGE: Please provide a message arguement') if (typeof options.message !== 'object') throw new TypeError('INVALID_MESSAGE: Invalid Discord Message object was provided.') if (!options.opponent) throw new TypeError('NO_OPPONENT: Please provide an opponent arguement') if (typeof options.opponent !== 'object') throw new TypeError('INVALID_OPPONENT: Invalid Discord User object was provided.') if (!options.embed) options.embed = {}; if (!options.embed.color) options.embed.color = '#5865f2'; if (typeof options.embed.color !== 'string') throw new TypeError('INVALID_COLOR: Embed Color must be a string.') if (!options.xEmoji) options.xEmoji = '❌'; if (typeof options.xEmoji !== 'string') throw new TypeError('INVALID_EMOJI: xEmoji should be a string.') if (!options.oEmoji) options.oEmoji = '🔵'; if (typeof options.oEmoji !== 'string') throw new TypeError('INVALID_EMOJI: oEmoji should be a string.') if (!options.xColor) options.xColor = 'red'; if (typeof options.xColor !== 'string') throw new TypeError('INVALID_COLOR: xColor should be a string.') if (!options.oColor) options.oColor = 'blurple'; if (typeof options.oColor !== 'string') throw new TypeError('INVALID_COLOR: oColor should be a string.') if (!options.askMessage) options.askMessage = 'Hey {opponent}, {challenger} challenged you for a game of Tic Tac Toe!'; if (typeof options.askMessage !== 'string') throw new TypeError('ASK_MESSAGE: Ask Messgae must be a string.') if (!options.cancelMessage) options.cancelMessage = 'Looks like they refused to have a game of Tic Tac Toe. \:('; if (typeof options.cancelMessage !== 'string') throw new TypeError('CANCEL_MESSAGE: Cancel Message must be a string.') if (!options.timeEndMessage) options.timeEndMessage = 'Since the opponent didnt answer, i dropped the game!'; if (typeof options.timeEndMessage !== 'string') throw new TypeError('TIME_END_MESSAGE: Time End Message must be a string.') if (!options.turnMessage) options.turnMessage = '{emoji} | Its now **{player}** turn!'; if (typeof options.turnMessage !== 'string') throw new TypeError('TURN_MESSAGE: Turn Message must be a string.') if (!options.waitMessage) options.waitMessage = 'Waiting for the opponent...'; if (typeof options.waitMessage !== 'string') throw new TypeError('WAIT_MESSAGE: Wait Message must be a string.') if (!options.gameEndMessage) options.gameEndMessage = 'The game went unfinished :('; if (typeof options.gameEndMessage !== 'string') throw new TypeError('GAME_END_MESSAGE: Game End Message must be a string.') if (!options.winMessage) options.winMessage = '{emoji} | **{winner}** won the game!'; if (typeof options.winMessage !== 'string') throw new TypeError('WIN_MESSAGE: Win Message must be a string.') if (!options.drawMessage) options.drawMessage = 'It was a draw!'; if (typeof options.drawMessage !== 'string') throw new TypeError('DRAW_MESSAGE: Draw Message must be a string.') this.options = options; this.message = options.message; this.opponent = options.opponent; this.gameBoard = [ [0, 0, 0], [0, 0, 0], [0, 0, 0] ]; this.inGame = false; this.xTurn = true; } async startGame() { if (this.inGame) return; const author = this.message.author; const opponent = this.opponent; const emoji = this.options.emoji ? this.options.emoji : ''; const noboyplay = new MessageEmbed().setTimestamp().setColor(this.options.embed.color).setAuthor("Tic Tac Toe", this.message.author.displayAvatarURL({ dynamic: true })).setDescription(`**${emoji}Sorry But You Cant Play With Bots!**`).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) const noyouplay = new MessageEmbed().setTimestamp().setColor(this.options.embed.color).setAuthor("Tic Tac Toe", this.message.author.displayAvatarURL({ dynamic: true })).setDescription(`**${emoji}Are You Alright?You Can Not Play With Yourself!**`).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) if (opponent.bot) return this.message.lineReplyNoMention(noboyplay) if (opponent.id === author.id) return this.message.lineReplyNoMention(noyouplay) const embed = new MessageEmbed().setTimestamp().setAuthor("Tic Tac Toe", this.message.author.displayAvatarURL({ dynamic: true })).setDescription(this.options.askMessage.replace('{challenger}', this.message.author).replace('{opponent}', this.opponent)).setColor(this.options.embed.color).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) let btn1 = new MessageButton().setLabel('Accept').setEmoji('✅').setStyle('green').setID('accept') let btn2 = new MessageButton().setLabel('Reject').setEmoji('❌').setStyle('red').setID('reject') let row = new MessageActionRow().addComponents(btn1, btn2) const askMsg = await this.message.channel.send({ embed: embed, components: [row] }); const filter = m => m.clicker.user.id === this.opponent.id; const interaction = await askMsg.createButtonCollector(filter, { time: 60000, }) interaction.on('collect', async btn => { await btn.reply.defer() if (btn.id === 'reject') { for (let y = 0; y < askMsg.components[0].components.length; y++) { askMsg.components[0].components[y].disabled = true; } askMsg.embeds[0].description = this.options.cancelMessage.replace('{opponent}', this.opponent).replace('{challenger}', this.message.author) return askMsg.edit({ embed: askMsg.embeds[0], components: askMsg.components }); } else if (btn.id === 'accept') { askMsg.delete().catch(); this.TTTGame(); } }) interaction.on("end", async(c, r) => { if (r === 'messageDelete') return; for (let y = 0; y < askMsg.components[0].components.length; y++) { askMsg.components[0].components[y].disabled = true; } askMsg.embeds[0].description = this.options.timeEndMessage.replace('{opponent}', this.opponent).replace('{challenger}', this.message.author); return askMsg.edit({ embed: askMsg.embeds[0], components: askMsg.components }); }) } async TTTGame() { this.inGame = true; var gameBoard = this.gameBoard; for (let y = 0; y < 3; y++) { for (let x = 0; x < 3; x++) { gameBoard[x][y] = NO_MOVE; } } let btn_a1 = 'a1_' + i(10) let btn_a2 = 'a2_' + i(10) let btn_a3 = 'a3_' + i(10) let btn_b1 = 'b1_' + i(10) let btn_b2 = 'b2_' + i(10) let btn_b3 = 'b3_' + i(10) let btn_c1 = 'c1_' + i(10) let btn_c2 = 'c2_' + i(10) let btn_c3 = 'c3_' + i(10) const embed = new MessageEmbed().setAuthor(`${this.message.author.username}vs${this.opponent.username}`).setDescription(this.options.turnMessage.replace('{emoji}', this.getChip()).replace('{player}', this.xTurn ? this.message.author.username : this.opponent.username)).setColor(this.options.embed.color).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) let a1 = new MessageButton().setID(btn_a1).setStyle("grey").setLabel("-") let a2 = new MessageButton().setID(btn_a2).setStyle("grey").setLabel("-") let a3 = new MessageButton().setID(btn_a3).setStyle("grey").setLabel("-") let b1 = new MessageButton().setID(btn_b1).setStyle("grey").setLabel("-") let b2 = new MessageButton().setID(btn_b2).setStyle("grey").setLabel("-") let b3 = new MessageButton().setID(btn_b3).setStyle("grey").setLabel("-") let c1 = new MessageButton().setID(btn_c1).setStyle("grey").setLabel("-") let c2 = new MessageButton().setID(btn_c2).setStyle("grey").setLabel("-") let c3 = new MessageButton().setID(btn_c3).setStyle("grey").setLabel("-") var msg = await this.message.channel.send({ embed: embed, components: [{ type: 1, components: [a1, a2, a3] }, { type: 1, components: [b1, b2, b3] }, { type: 1, components: [c1, c2, c3] }, ] }) const filter = m => m.clicker.user.id == this.message.author.id || m.clicker.user.id == this.opponent.id; const collector = msg.createButtonCollector(filter, { idle: 120000, }) collector.on('collect', async btn => { const turn = this.xTurn ? this.message.author.id : this.opponent.id; if (btn.clicker.user.id !== turn) { return btn.reply.send(this.options.waitMessage, true) } await btn.reply.defer(); const index = reactions[btn.id.split('_')[0]] - 1; const x = index % 3; const y = Math.floor(index / 3); const ebtn = msg.components[y].components[x]; var new_btn = new MessageButton().setID(ebtn.custom_id).setDisabled().setEmoji(this.getChip()).setStyle(this.xTurn ? this.options.xColor : this.options.oColor) msg.components[y].components[x] = new_btn; this.placeMove(x, y, this.xTurn ? PLAYER_1 : PLAYER_2) if (this.isGameOver()) { if (this.hasWon(PLAYER_2)) this.gameOver({ result: "winner", name: this.opponent.username, emoji: this.getChip() }, msg); else if (this.hasWon(PLAYER_1)) this.gameOver({ result: "winner", name: this.message.author.username, emoji: this.getChip() }, msg) else this.gameOver({ result: "tie" }, msg) } else { this.xTurn = !this.xTurn; const edit_embed = msg.embeds[0].setDescription(this.options.turnMessage.replace('{emoji}', this.getChip()).replace('{player}', this.xTurn ? this.message.author.username : this.opponent.username)).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) msg.edit({ embed: edit_embed, components: msg.components }) } }) collector.on("end", async(c, r) => { if (r === 'idle' && this.inGame === true) this.gameOver({ result: "timeout" }, msg) }) } gameOver(result, msg) { this.inGame = false; const Embed = new MessageEmbed().setColor(msg.embeds[0].color).setAuthor(`${this.message.author.username}vs${this.opponent.username}`).setDescription("**Game Over!** - " + this.getResultText(result)).setFooter('XOPPACK©', this.message.author.displayAvatarURL({ dynamic: true })) for (let x = 0; x < msg.components.length; x++) { for (let y = 0; y < msg.components[x].components.length; y++) { msg.components[x].components[y].disabled = true; } } return msg.edit({ embed: Embed, components: msg.components }) } getChip() { return this.xTurn ? this.options.xEmoji : this.options.oEmoji; } placeMove(x, y, player) { this.gameBoard[x][y] = player; } isGameOver() { if (this.hasWon(PLAYER_1) || this.hasWon(PLAYER_2)) return true; if (this.AvailablePoints().length == 0) { return true; } return false; } hasWon(player) { var gameBoard = this.gameBoard; if (gameBoard[0][0] == gameBoard[1][1] && gameBoard[0][0] == gameBoard[2][2] && gameBoard[0][0] == player) { return true; } if (gameBoard[0][2] == gameBoard[1][1] && gameBoard[0][2] == gameBoard[2][0] && gameBoard[0][2] == player) { return true; } for (let i = 0; i < 3; ++i) { if (gameBoard[i][0] == gameBoard[i][1] && gameBoard[i][0] == gameBoard[i][2] && gameBoard[i][0] == player) { return true; } if (gameBoard[0][i] == gameBoard[1][i] && gameBoard[0][i] == gameBoard[2][i] && gameBoard[0][i] == player) { return true; } } return false; } AvailablePoints() { const availablePoints = []; for (let i = 0; i < 3; ++i) for (let j = 0; j < 3; ++j) if (this.gameBoard[i][j] == NO_MOVE) availablePoints.push({ x: i, y: j }); return availablePoints; } getResultText(result) { if (result.result === 'tie') return this.options.drawMessage; else if (result.result === 'timeout') return this.options.gameEndMessage; else if (result.result === 'error') return 'ERROR: ' + result.error; else return this.options.winMessage.replace('{emoji}', result.emoji).replace('{winner}', result.name); } } module.exports = TicTacToe; function i(length) { var randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var result = ''; for (var i = 0; i < length; i++) { result += randomChars.charAt(Math.floor(Math.random() * randomChars.length)) } return result; }