UNPKG

discord-mini-games-es.js

Version:

Un paquete para implementar minijuegos usando discord.js-v14 en español

172 lines (169 loc) 9.35 kB
const discord = require('discord.js'); const {EmbedBuilder,ButtonBuilder,ButtonStyle,ActionRowBuilder,ComponentType} = require('discord.js') class TicTacToe{ /** * Initialises a new instance of Tic Tac Toe Game. * @param {`Message/Interaction`} message The Message Object. * @param {`GameOptions-Object`} gameOptions The Game Options Object. * @returns {TicTacToe} Game instance. */ constructor(message,gameOptions) { if(!message) throw new Error("el mensaje no se proporciono"); this.message = message; if(gameOptions && typeof gameOptions !== 'object') throw new TypeError("gameOptions debe ser un objeto"); this.isSlash = gameOptions?.isSlash ?? false; if(this.isSlash == true){ if(!(this.message instanceof discord.CommandInteraction)){ throw new TypeError("message debe ser una instancia de Command Interaction") } } else { if(!(this.message instanceof discord.Message)) { throw new TypeError("message debe ser una instancia de Discord Message") } } this.player = this.isSlash == true ? this.message?.user : this.message?.author; this.opponent = gameOptions?.opponent ?? null; if(!this.opponent) throw new Error("El oponente no se proporciono"); if(this.opponent && !(this.opponent instanceof discord.User)) { throw new TypeError("opponent debe ser una instancia de Discord User"); } this.time = gameOptions?.time ?? 120000; this.replied = false; this.randomN = (min,max) => {return Math.floor(Math.random()*max)+min;} this.edit = async (messageOptions,replyMessage) => { messageOptions.fetchReply = true; if(this.replied == false) { this.replied=true; if(this.isSlash == true) return replyMessage.editReply(messageOptions) return await this.message.reply(messageOptions);} else return await replyMessage.edit(messageOptions) } this.options = gameOptions; this.onWin = gameOptions?.onWin ?? null; this.onTie = gameOptions?.onTie ?? null; this.onTimeUp = gameOptions?.onTimeUp ?? null; if(this.player.id == this.opponent.id) throw new Error("player and opponent cannot be same"); if(this.options?.playerEmoji && typeof this.options?.playerEmoji !== 'string') throw new TypeError('playerEmoji debe de ser un String'); if(this.options?.opEmoji && typeof this.options?.opEmoji !== 'string') throw new TypeError('opEmoji debe de ser un String'); if(this.options?.emptyEmoji && typeof this.options?.emptyEmoji !== 'string') throw new TypeError('emptyEmoji debe de ser un String'); if(this.onWin && typeof this.onWin !== 'function') throw new TypeError('onWin debe de ser una Función'); if(this.onTie && typeof this.onTie !== 'function') throw new TypeError('onTie debe de ser una Función'); if(this.onTimeUp && typeof this.onTimeUp !== 'function') throw new TypeError('onTimeUp debe de ser una Función'); if(typeof this.isSlash !== 'boolean') throw new TypeError('isSlash debe de ser Boolean'); if(typeof this.time !== 'number') throw new TypeError('time debe de ser un Número'); if(this.time < 5000) throw new RangeError('time debe ser mayor que 5000'); if(this.options?.title && typeof this.options?.title !== 'string') throw new TypeError('title debe de ser un String'); if(this.options?.startDes && typeof this.options?.startDes !== 'string') throw new TypeError('startDes debe de ser un String'); if(this.options?.winDes && typeof this.options?.winDes !== 'string') throw new TypeError('winDes debe de ser un String'); if(this.options?.retryDes && typeof this.options?.retryDes !== 'string') throw new TypeError('retryDes debe de ser un String'); if(this.options?.timeUpDes && typeof this.options?.timeUpDes !== 'string') throw new TypeError('timeUpDes debe de ser un String'); if(this.options?.confirmDes && typeof this.options?.confirmDes !== 'string') throw new TypeError('confirmDes debe de ser un String'); if(this.options?.declineDes && typeof this.options?.declineDes !== 'string') throw new TypeError('declineDes debe de ser un String'); if(this.options?.noResDes && typeof this.options?.noResDes !== 'string') throw new TypeError('noResDes debe de ser un String'); if(this.options?.tieDes && typeof this.options?.tieDes !== 'string') throw new TypeError('tieDes debe de ser un String'); } /** * Starts the game */ async run() { if(this.isSlash == true) { await this.message.deferReply(); } const game = this; function Embed(des,color) { return new EmbedBuilder() .setDescription(des) .setColor(color) .setTimestamp() .setThumbnail(game.player.avatarURL()) .setTitle(game.options?.title ?? "Tic Tac Toe") .setFooter({text:game.options?.footer ?? `${game.player.username} vs ${game.opponent.username}`}) } function verifyGame(board) { const win = [ ['00','01','02'], ['10','11','12'], ['20','21','22'], ['00','10','20'], ['01','11','21'], ['02','12','22'], ['00','11','22'], ['02','11','20'] ] for (let i = 0; i < win.length; i++) { const [a,b,c] = win[i]; if(!board.get(a) || !board.get(b) || !board.get(c)) continue; if(board.get(a) == board.get(b) && board.get(b) == board.get(c)) return 'win'; } return board.size == 9 ? 'tie' : 'continue'; } const msg = await this.edit({content:`${this.opponent}`,embeds:[Embed(this.options?.confirmDes ?? `${this.player} te ha retado a una partida de tres en raya`,'Aqua')], components:[ new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('yes').setLabel('Aceptar').setStyle(ButtonStyle.Success), new ButtonBuilder().setCustomId('no').setLabel('Rechazar').setStyle(ButtonStyle.Danger)) ]},this.message) const filter = (i) => i.user.id == this.opponent.id try { var gameBoard = new discord.Collection(); var played = false; const i = await msg.awaitMessageComponent({filter:filter,time:this.options?.resTime ?? 30000}) i.deferUpdate(); let emojis = {}; function Buttons(x,y,z) { return new ButtonBuilder() .setCustomId(x) .setEmoji(y ?? game.options?.emptyEmoji ?? "◼️") .setStyle(ButtonStyle.Secondary) .setDisabled(z ?? false) } if(i.customId == "yes") { var Rows = [ new ActionRowBuilder().addComponents(Buttons('ttt_00'),Buttons('ttt_01'),Buttons('ttt_02')), new ActionRowBuilder().addComponents(Buttons('ttt_10'),Buttons('ttt_11'),Buttons('ttt_12')), new ActionRowBuilder().addComponents(Buttons('ttt_20'),Buttons('ttt_21'),Buttons('ttt_22')) ] emojis[this.player.id] = this.options?.playerEmoji ?? "❌"; emojis[this.opponent.id] = this.options?.opEmoji ?? "🟢"; var chances = [[this.player,this.opponent],[this.opponent,this.player]][this.randomN(0,1)] await this.edit({content:"",embeds: [ Embed(this.options?.nextDes?.replace(/{next_player}/g,chances[0])?.replace(/{emoji}/g,emojis[chances[0].id]) ?? `${emojis[chances[0].id]} ${chances[0]}'s turn`,'Aqua')],components:Rows},msg) const filter2 = (i) => i.user.id == this.opponent.id || i.user.id == this.player.id const collector = msg.createMessageComponentCollector({filter:filter2,ComponentType:ComponentType.Button,idle:this.time}) collector.on('collect', async i => { await i.deferUpdate(); if(i.user.id !== chances[0].id) return; Rows[i.customId[4]].components.find(x => x.data.custom_id == i.customId).setStyle(i.user.id == this.player.id ? ButtonStyle.Danger : ButtonStyle.Success).setEmoji(emojis[i.user.id]).setDisabled(true) gameBoard.set(i.customId.replace('ttt_',''),i.user.id) chances = chances.reverse(); const wonGame = verifyGame(gameBoard); if(wonGame == 'continue') { await this.edit({embeds: [ Embed(this.options?.nextDes?.replace(/{next_player}/g,chances[0])?.replace(/{emoji}/g,`${emojis[chances[0].id]}`) ?? `${emojis[chances[0].id]} ${chances[0]}'s turn`,'Aqua')],components:Rows},msg) } if(wonGame == 'win') { played = true; collector.stop(); await this.edit({embeds: [Embed(this.options?.winDes?.replace(/{winner}/g,i.user)?.replace(/{emoji}/g,emojis[i.user.id]) ?? `${emojis[i.user.id]} ${i.user} Won!`,'Green')],components:Rows},msg) if(this.onWin) await this.onWin(i.user,this.player.id == i.user.id ? this.opponent : this.player) } if(wonGame == 'tie') { played = true; collector.stop(); await this.edit({embeds: [Embed(this.options?.tieDes ?? 'Juego empatado','Yellow')],components:Rows},msg); if(this.onTie) await this.onTie(); } }) collector.on('end', async () => { if(played == true) return; await this.edit({embeds: [Embed(this.options?.timeUpDes?.replace(/{timed_player}/g,chances[0])?.replace(/{emoji}/g,emojis[chances[0].id]) ?? `Juego terminado: ${emojis[chances[0].id]} ${chances[0]} tardó demasiado en responder`,'Red')]},msg) if(this.onTimeUp) await this.onTimeUp(chances[0], chances[1]); }) } else { await this.edit({ content:"", embeds:[ Embed(this.options?.declineDes ?? `${this.opponent} ha rechazado tu reto`,'Red')],components:[]},msg) } } catch(e) { await this.edit({ content:"", embeds: [ Embed(this.options?.noResDes ?? `${this.opponent} no respondió a tiempo`,'Red')], components:[]},msg) } } } module.exports = TicTacToe;