diff --git a/.gitignore b/.gitignore index fed401d..8588c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ package-lock.json +test/test.js node_modules/ \ No newline at end of file diff --git a/package.json b/package.json index e8b42b3..ff62e5e 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,51 @@ -{ - "name": "flex-gamecord", - "version": "4.2.1-dev", - "description": "Discord Gamecord is a powerful npm package with a collection of minigames for your discord bot", - "main": "index.js", - "scripts": { - "test": "cd test && node ." - }, - "author": "Aniket#0729", - "contributor": "Mr5ecret", - "license": "MIT", - "dependencies": { - "discord.js": "^14.6.0", - "html-entities": "^2.3.3", - "node-fetch": "^2.6.7" - }, - "repository": { - "type": "git", - "url": "git+git@github.com:Mr5ecret/Flex-Gamecord.git" - }, - "homepage": "https://discord-gamecord.js.org/", - "keywords": [ - "discord-gamecord", - "discord-games", - "discord.js", - "minigames", - "paeonic" - ], - "bugs": { - "url": "https://github.com/Mr5ecret/Flex-Gamecord/issues" - }, - "directories": { - "src": "src" - } -} +{ + "name": "flex-gamecord", + "version": "4.2.2", + "description": "Discord Gamecord is a powerful npm package with a collection of minigames for your discord bot", + "main": "index.js", + "scripts": { + "test": "cd test && node ." + }, + "authors": [ + { + "name": "Paeonic-Development", + "url": "https://github.com/Paeonic-Development" + }, + { + "name": "Mr5ecret", + "url": "https://github.com/Mr5ecret" + } + ], + "contributors": [ + { + "name": "aniket091", + "url": "https://github.com/aniket091" + } + ], + "contributor": "Mr5ecret", + "license": "MIT", + "dependencies": { + "canvas": "^2.11.2", + "discord.js": "^14.14.1", + "html-entities": "^2.3.3", + "node-fetch": "^2.6.7" + }, + "repository": { + "type": "git", + "url": "git+git@github.com:Paeonic-Development/Flex-Gamecord.git" + }, + "homepage": "https://github.com/Paeonic-Development/Flex-Gamecord", + "keywords": [ + "discord-gamecord", + "discord-games", + "discord.js", + "minigames", + "paeonic" + ], + "bugs": { + "url": "https://github.com/Paeonic-Development/Flex-Gamecord/issues" + }, + "directories": { + "src": "src" + } +} diff --git a/src/2048.js b/src/2048.js index 6f1d65d..94078c4 100644 --- a/src/2048.js +++ b/src/2048.js @@ -1,8 +1,8 @@ const { EmbedBuilder, ActionRowBuilder, AttachmentBuilder } = require('discord.js'); const { disableButtons, formatMessage, move, oppDirection, ButtonBuilder } = require('../utils/utils'); -const chars = '0123456789abcdefghijklmnopqrstuvwxyz'; const events = require('events'); - +const { createCanvas, loadImage } = require('canvas'); +const path = require('path'); module.exports = class TwoZeroFourEight extends events { constructor(options = {}) { @@ -12,7 +12,6 @@ module.exports = class TwoZeroFourEight extends events { if (typeof options.message !== 'object') throw new TypeError('INVALID_MESSAGE: message option must be an object.'); if (typeof options.isSlashGame !== 'boolean') throw new TypeError('INVALID_COMMAND_TYPE: isSlashGame option must be a boolean.'); - if (!options.embed) options.embed = {}; if (!options.embed.title) options.embed.title = '2048'; if (!options.embed.color) options.embed.color = '#5865F2'; @@ -22,11 +21,10 @@ module.exports = class TwoZeroFourEight extends events { if (!options.emojis.down) options.emojis.down = '⬇️'; if (!options.emojis.left) options.emojis.left = '⬅️'; if (!options.emojis.right) options.emojis.right = '➡️'; - + if (!options.timeoutTime) options.timeoutTime = 60000; if (!options.buttonStyle) options.buttonStyle = 'PRIMARY'; - if (typeof options.embed !== 'object') throw new TypeError('INVALID_EMBED: embed option must be an object.'); if (typeof options.embed.title !== 'string') throw new TypeError('INVALID_EMBED: embed title must be a string.'); if (typeof options.embed.color !== 'string') throw new TypeError('INVALID_EMBED: embed color must be a string.'); @@ -42,7 +40,6 @@ module.exports = class TwoZeroFourEight extends events { if (typeof options.playerOnlyMessage !== 'string') throw new TypeError('INVALID_MESSAGE: playerOnlyMessage option must be a string.'); } - super(); this.options = options; this.message = options.message; @@ -58,21 +55,45 @@ module.exports = class TwoZeroFourEight extends events { } } - async sendMessage(content) { if (this.options.isSlashGame) return await this.message.editReply(content); else return await this.message.channel.send(content); } async getBoardImage() { - const url = 'https://api.aniket091.xyz/2048?board=' + this.gameBoard.map(c => chars[c]).join(''); - return await new AttachmentBuilder(url, { name: 'gameboard.png' }); - } + const canvas = createCanvas(this.length * 100, this.length * 100); + const ctx = canvas.getContext('2d'); + const imageFilenames = [ + '0.png', '2.png', '4.png', '8.png', '16.png', '32.png', '64.png', '128.png', '256.png', '512.png', '1024.png', '2048.png', + ]; + + const images = {}; + for (let i = 0; i < imageFilenames.length; i++) { + const imagePath = path.join(__dirname, 'boardImages', imageFilenames[i]); + images[i] = await loadImage(imagePath).catch(err => { + console.error(`Error loading image: ${imagePath}`); + throw err; + }); + } + + for (let y = 0; y < this.length; y++) { + for (let x = 0; x < this.length; x++) { + const tileValue = this.gameBoard[y * this.length + x]; + const tileImage = images[tileValue]; + + if (tileImage) { + ctx.drawImage(tileImage, x * 100, y * 100, 100, 100); + } + } + } + const buffer = canvas.toBuffer(); + return new AttachmentBuilder(buffer, { name: 'gameboard.png' }); + } async startGame() { if (this.options.isSlashGame || !this.message.author) { - if (!this.message.deferred) await this.message.deferReply().catch(e => {}); + if (!this.message.deferred) await this.message.deferReply().catch(e => { }); this.message.author = this.message.user; this.options.isSlashGame = true; } @@ -81,11 +102,11 @@ module.exports = class TwoZeroFourEight extends events { const embed = new EmbedBuilder() - .setTitle(this.options.embed.title) - .setColor(this.options.embed.color) - .setImage('attachment://gameboard.png') - .addFields({ name: 'Current Score', value: this.score.toString() }) - .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); + .setTitle(this.options.embed.title) + .setColor(this.options.embed.color) + .setImage('attachment://gameboard.png') + .addFields({ name: 'Current Score', value: this.score.toString() }) + .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); const up = new ButtonBuilder().setEmoji(this.options.emojis.up).setStyle(this.options.buttonStyle).setCustomId('2048_up'); @@ -115,7 +136,7 @@ module.exports = class TwoZeroFourEight extends events { collector.on('collect', async btn => { - await btn.deferUpdate().catch(e => {}); + await btn.deferUpdate().catch(e => { }); if (btn.user.id !== this.message.author.id) { if (this.options.playerOnlyMessage) btn.followUp({ content: formatMessage(this.options, 'playerOnlyMessage'), ephemeral: true }); return; @@ -132,11 +153,11 @@ module.exports = class TwoZeroFourEight extends events { const embed = new EmbedBuilder() - .setTitle(this.options.embed.title) - .setColor(this.options.embed.color) - .setImage('attachment://gameboard.png') - .addFields({ name: 'Current Score', value: this.score.toString() }) - .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); + .setTitle(this.options.embed.title) + .setColor(this.options.embed.color) + .setImage('attachment://gameboard.png') + .addFields({ name: 'Current Score', value: this.score.toString() }) + .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); return msg.edit({ embeds: [embed], files: [await this.getBoardImage()], attachments: [] }); }) @@ -154,11 +175,11 @@ module.exports = class TwoZeroFourEight extends events { this.emit('gameOver', { result: (result ? 'win' : 'lose'), ...TwoZeroFourEightGame }); const embed = new EmbedBuilder() - .setTitle(this.options.embed.title) - .setColor(this.options.embed.color) - .setImage('attachment://gameboard.png') - .addFields({ name: 'Total Score', value: this.score.toString() }) - .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); + .setTitle(this.options.embed.title) + .setColor(this.options.embed.color) + .setImage('attachment://gameboard.png') + .addFields({ name: 'Total Score', value: this.score.toString() }) + .setFooter({ text: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); return msg.edit({ embeds: [embed], components: disableButtons(msg.components), files: [await this.getBoardImage()], attachments: [] }); } @@ -174,7 +195,7 @@ module.exports = class TwoZeroFourEight extends events { const posNum = this.gameBoard[y * this.length + x]; ['down', 'left', 'right', 'up'].forEach(dir => { - const newPos = move({x, y}, dir); + const newPos = move({ x, y }, dir); if (this.isInsideBlock(newPos) && (this.gameBoard[newPos.y * this.length + newPos.x] === 0 || this.gameBoard[newPos.y * this.length + newPos.x] === posNum)) numMoves++; }) } diff --git a/src/MatchPairs.js b/src/MatchPairs.js index 7a31b2f..9a92ee6 100644 --- a/src/MatchPairs.js +++ b/src/MatchPairs.js @@ -20,8 +20,8 @@ module.exports = class MatchPairs extends events { if (!options.timeoutTime) options.timeoutTime = 60000; if (!options.emojis) options.emojis = ['🍉', '🍇', '🍊', '🍋', '🥭', '🍎', '🍏', '🥝', '🥥', '🍓', '🍒', '🫐', '🍍', '🍅', '🍐', '🥔', '🌽', '🥕', '🥬', '🥦']; if (!options.winMessage) options.winMessage = '**You won the Game! You turned a total of `{tilesTurned}` tiles.**'; - if (!options.loseMessage) options.loseMessage = '**You lost the Game! You turned a total of `{tilesTurned}` tiles.**'; - + if (!options.loseMessage) options.loseMessage = '**You lost the Game! You turned a total of `{tilesTurned}` tiles.**'; + if (typeof options.embed !== 'object') throw new TypeError('INVALID_EMBED: embed option must be an object.'); if (typeof options.embed.title !== 'string') throw new TypeError('INVALID_EMBED: embed title must be a string.'); @@ -37,7 +37,7 @@ module.exports = class MatchPairs extends events { if (typeof options.playerOnlyMessage !== 'string') throw new TypeError('INVALID_MESSAGE: playerOnly Message option must be a string.'); } - + super(); this.options = options; this.message = options.message; @@ -58,11 +58,11 @@ module.exports = class MatchPairs extends events { async startGame() { if (this.options.isSlashGame || !this.message.author) { - if (!this.message.deferred) await this.message.deferReply().catch(e => {}); + if (!this.message.deferred) await this.message.deferReply().catch(e => { }); this.message.author = this.message.user; this.options.isSlashGame = true; } - + this.emojis = shuffleArray(this.emojis).slice(0, 12); this.emojis.push(...this.emojis, '🃏'); this.emojis = shuffleArray(this.emojis); @@ -70,10 +70,10 @@ module.exports = class MatchPairs extends events { const embed = new EmbedBuilder() - .setColor(this.options.embed.color) - .setTitle(this.options.embed.title) - .setDescription(this.options.embed.description) - .setAuthor({ name: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); + .setColor(this.options.embed.color) + .setTitle(this.options.embed.title) + .setDescription(this.options.embed.description) + .setAuthor({ name: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); const msg = await this.sendMessage({ embeds: [embed], components: this.components }); return this.handleButtons(msg); @@ -113,10 +113,10 @@ module.exports = class MatchPairs extends events { const embed = new EmbedBuilder() - .setColor(this.options.embed.color) - .setTitle(this.options.embed.title) - .setDescription(GameOverMessage.replace('{tilesTurned}', this.tilesTurned)) - .setAuthor({ name: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); + .setColor(this.options.embed.color) + .setTitle(this.options.embed.title) + .setDescription(GameOverMessage.replace('{tilesTurned}', this.tilesTurned)) + .setAuthor({ name: this.message.author.tag, iconURL: this.message.author.displayAvatarURL({ dynamic: true }) }); return msg.edit({ embeds: [embed], components: disableButtons(this.components) }); } @@ -126,7 +126,7 @@ module.exports = class MatchPairs extends events { const collector = msg.createMessageComponentCollector({ idle: this.options.time }); collector.on('collect', async btn => { - await btn.deferUpdate().catch(e => {}); + await btn.deferUpdate().catch(e => { }); if (btn.user.id !== this.message.author.id) { if (this.options.playerOnlyMessage) btn.followUp({ content: formatMessage(this.options, 'playerOnlyMessage'), ephemeral: true }); return; @@ -140,7 +140,7 @@ module.exports = class MatchPairs extends events { const emojiBtn = this.components[y].components[x]; this.tilesTurned += 1; - + if (!this.selected) { this.selected = { x: x, y: y, id: id }; emojiBtn.setEmoji(emoji).setStyle('PRIMARY').removeLabel(); @@ -171,7 +171,7 @@ module.exports = class MatchPairs extends events { await msg.edit({ components: this.components }); emojiBtn.removeEmoji().setStyle('SECONDARY').setLabel('\u200b'); selectedBtn.removeEmoji().setStyle('SECONDARY').setLabel('\u200b'); - return this.selected = null;; + return this.selected = null; } this.remainingPairs -= 1; @@ -182,7 +182,7 @@ module.exports = class MatchPairs extends events { if (this.remainingPairs === 0) return collector.stop(); return await msg.edit({ components: this.components }); }) - + collector.on('end', async (_, reason) => { if (reason === 'idle') return this.gameOver(msg, false); diff --git a/src/boardImages/0.png b/src/boardImages/0.png new file mode 100644 index 0000000..7b71db1 Binary files /dev/null and b/src/boardImages/0.png differ diff --git a/src/boardImages/1024.png b/src/boardImages/1024.png new file mode 100644 index 0000000..c67a297 Binary files /dev/null and b/src/boardImages/1024.png differ diff --git a/src/boardImages/128.png b/src/boardImages/128.png new file mode 100644 index 0000000..584f04b Binary files /dev/null and b/src/boardImages/128.png differ diff --git a/src/boardImages/16.png b/src/boardImages/16.png new file mode 100644 index 0000000..35a070a Binary files /dev/null and b/src/boardImages/16.png differ diff --git a/src/boardImages/2.png b/src/boardImages/2.png new file mode 100644 index 0000000..c4c08dc Binary files /dev/null and b/src/boardImages/2.png differ diff --git a/src/boardImages/2048.png b/src/boardImages/2048.png new file mode 100644 index 0000000..92445f9 Binary files /dev/null and b/src/boardImages/2048.png differ diff --git a/src/boardImages/256.png b/src/boardImages/256.png new file mode 100644 index 0000000..c18c307 Binary files /dev/null and b/src/boardImages/256.png differ diff --git a/src/boardImages/32.png b/src/boardImages/32.png new file mode 100644 index 0000000..bc3f6cb Binary files /dev/null and b/src/boardImages/32.png differ diff --git a/src/boardImages/4.png b/src/boardImages/4.png new file mode 100644 index 0000000..4b71ebc Binary files /dev/null and b/src/boardImages/4.png differ diff --git a/src/boardImages/512.png b/src/boardImages/512.png new file mode 100644 index 0000000..4802305 Binary files /dev/null and b/src/boardImages/512.png differ diff --git a/src/boardImages/64.png b/src/boardImages/64.png new file mode 100644 index 0000000..809dd9a Binary files /dev/null and b/src/boardImages/64.png differ diff --git a/src/boardImages/8.png b/src/boardImages/8.png new file mode 100644 index 0000000..80875b1 Binary files /dev/null and b/src/boardImages/8.png differ diff --git a/test/index.js b/test/index.js index 1dcd076..e829157 100644 --- a/test/index.js +++ b/test/index.js @@ -1,10 +1,10 @@ const Discord = require('discord.js'); -const client = new Discord.Client({ intents: [ 1, 512, 4096, 32768 ] }); +const client = new Discord.Client({ intents: [1, 512, 4096, 32768] }); const { Snake } = require('../index'); client.on('messageCreate', async (message) => { - if(message.content === '!snake') { + if (message.content === '!snake') { const Game = new Snake({ message: message, isSlashGame: false, @@ -16,7 +16,7 @@ client.on('messageCreate', async (message) => { emojis: { board: '⬛', food: '🍎', - up: '⬆️', + up: '⬆️', down: '⬇️', left: '⬅️', right: '➡️', @@ -27,7 +27,7 @@ client.on('messageCreate', async (message) => { foods: ['🍎', '🍇', '🍊', '🫐', '🥕', '🥝', '🌽'], playerOnlyMessage: 'Only {player} can use these buttons.' }); - + Game.startGame(); Game.on('gameOver', result => { console.log(result);