diff --git a/commands/db/battle.js b/commands/db/battle.js index dd89ee5..92e6487 100644 --- a/commands/db/battle.js +++ b/commands/db/battle.js @@ -1,46 +1,101 @@ //@ts-check -const { MessageActionRow, MessageButton, MessageSelectMenu } = require('discord.js'); +const { MessageActionRow, MessageButton, MessageSelectMenu, Client, CommandInteractionOptionResolver } = require('discord.js'); const { STATE } = require('./econ'); const { winGame, getCustomEmoji } = require('./external_game_functions.js'); const { changeTurn } = require('../turnManager.js'); -const { default: mongoose } = require('mongoose'); +const { game_class_battle } = require('./game_classes'); +const { MongoClient } = require('mongodb'); +const { convertSnowflakeToDate } = require('./addons/snowflake'); function postActionBar(thread, user_dbo) { - const row = new MessageActionRow() - .addComponents( - new MessageButton() - .setCustomId('ATTACK') - .setLabel('ATTACK') - .setStyle('DANGER'), - new MessageButton() - .setCustomId('HEAL') - .setLabel('HEAL') - .setStyle('SUCCESS'), - new MessageButton() - .setCustomId('DEFEND') - .setLabel('DEFEND') - .setStyle('PRIMARY'), - new MessageButton() - .setCustomId('ITEMS') - .setLabel('ITEMS') - .setStyle('SECONDARY') - ); + user_dbo.find({'hpmp.hp': {$exists: true}}).toArray((err, docs) => { + const hp = docs[0].hpmp.hp; + const mp = docs[0].hpmp.mp; + let row; + + if (docs[0].gamesettings.battle.class != 'none' && docs[0].gamesettings.battle.ultimate) { + row = new MessageActionRow() + .addComponents( + new MessageButton() + .setCustomId('ATTACK') + .setLabel('ATTACK') + .setStyle('DANGER'), + new MessageButton() + .setCustomId('HEAL') + .setLabel('HEAL') + .setStyle('SUCCESS'), + new MessageButton() + .setCustomId('DEFEND') + .setLabel('DEFEND') + .setStyle('PRIMARY'), + new MessageButton() + .setCustomId('ULTIMATE') + .setLabel('ULTIMATE') + .setStyle('DANGER') + ); + } else { + //If the ultimate can't be used, change the menu + row = new MessageActionRow() + .addComponents( + new MessageButton() + .setCustomId('ATTACK') + .setLabel('ATTACK') + .setStyle('DANGER'), + new MessageButton() + .setCustomId('HEAL') + .setLabel('HEAL') + .setStyle('SUCCESS'), + new MessageButton() + .setCustomId('DEFEND') + .setLabel('DEFEND') + .setStyle('PRIMARY'), + ); + } + /* + //UNDER DEVELOPMENT + new MessageButton() + .setCustomId('ITEMS') + .setLabel('ITEMS') + .setStyle('SECONDARY') + */ - thread.send({ content: `Your turn <@${user_dbo.s.namespace.collection}>!`, components: [row] }); + thread.send({ content: `Your turn <@${user_dbo.s.namespace.collection}>!\nHP: ${hp}\t|\tMP: ${mp}`, components: [row] }); + }) } /** * Called by "attack" + * @param {game_class_battle} gclass */ -function attack_special() { +function attack_special(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction, gclass) { + const atk = gclass.specialAttack; + if (!atk) { return thread.send("You don't have a class, and so can't use an ultimate!"); } + + user_dbo.find({'rank': {$exists: true}}).toArray((err, docs) => { + const doc = docs[0]; + //Check if the user can use ultimate + if (atk.dmg.split('*')[0] == 'r') { + const rank = doc.rank; + const dmg = Number(atk.dmg.split('*')[1]) * rank; + attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction, dmg); + } + }) + + //Apply a "stunned" effect + if (atk.prone == true) { + other_dbo.updateOne({'state': {$exists: true}}, {$set: {state: STATE.PRONE}}); + thread.send(`<@${interaction.user.id}> was knocked prone and lost 1 turn!`); + } + + changeTurn(client, bot, interaction); } //Bow special phrase: Σ>―(´・ω・`)→ -function attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction) { +function attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction, preset_damage = 0) { //Get the weapon user_dbo.find({'equipped': {$exists: true}}).toArray(function(err, docs) { const doc = docs[0]; @@ -50,11 +105,15 @@ function attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interac var dmg = 0; //No weapons (punch) - if (weapon == null) { - dmg = doc.rank; + if (preset_damage > 0) { + dmg = preset_damage; } else { - dmg = (doc.rank - 1) + Math.round(weapon.cost/5); - } + if (weapon == null) { + dmg = doc.rank; + } else { + dmg = (doc.rank - 1) + Math.round(weapon.cost/5); + } + } other_dbo.find({'equipped': {$exists: true}}).toArray(function (err, docs) { const odoc = docs[0]; @@ -72,17 +131,16 @@ function attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interac if (new_hp <= 0) { winGame(client, bot, client.db(user_dbo.s.namespace.db), user_dbo, xp_collection, interaction.message); } else { - other_dbo.updateOne({'equipped': {$exists: true}}, { $set: { 'hpmp.hp' :new_hp, state: STATE.FIGHTING }}); + other_dbo.updateOne({'equipped': {$exists: true}}, { $set: { 'hpmp.hp': new_hp, state: STATE.FIGHTING }}); + + //Change turns + changeTurn(client, bot, interaction); } }); }) //Check for a "special" animation - - - //Change turns - changeTurn(client, bot, interaction); } @@ -97,11 +155,16 @@ async function heal(interaction, client, user_dbo, bot, thread, command, mongour user_dbo.find({'equipped': {$exists: true}}).toArray(async function(err, docs) { const doc = docs[0]; const rawitems = doc.equipped.items; + if (JSON.stringify(rawitems) == '{}') { + interaction.editReply("You don't have any items!"); + return postActionBar(thread, user_dbo); + } + + console.log(rawitems); const items = rawitems.filter(function(f) { return (f.sect.toLowerCase() == 'hp') }); - if (JSON.stringify(items) == '[]') { - interaction.editReply("You don't have any items!"); + interaction.editReply("You don't have any healing items!"); return postActionBar(thread, user_dbo); } else { console.log(JSON.stringify(items))} @@ -123,18 +186,6 @@ async function heal(interaction, client, user_dbo, bot, thread, command, mongour .setCustomId(`${interaction.user.id}|heal`) .setPlaceholder('Nothing selected') .addOptions(itemlist), - // .addOptions([ - // { - // label: 'Select me', - // description: 'This is a description', - // value: 'first_option', - // }, - // { - // label: 'You can select me too', - // description: 'This is also a description', - // value: 'second_option', - // }, - // ]) ); await interaction.editReply({ content: 'Please choose a health potion!', components: [row] }); @@ -171,12 +222,12 @@ function presentItems(interaction, client, user_dbo, bot, thread) { const row = new MessageActionRow() .addComponents( new MessageSelectMenu() - .setCustomId(`${interaction.user.id}|heal`) + .setCustomId(`${interaction.user.id}|item`) .setPlaceholder('Nothing selected') .addOptions(itemlist) ); - await interaction.editReply({ content: 'Please choose a health potion!', components: [row] }); + await interaction.editReply({ content: 'Please choose an item!', components: [row] }); }); } @@ -184,21 +235,19 @@ function presentItems(interaction, client, user_dbo, bot, thread) { function defend(client, interaction, user_dbo, bot, thread) { user_dbo.find({'equipped': {$exists: true}}).toArray(function(err, docs) { const doc = docs[0]; - const all_weapons = doc.get('weapons'); - const shield = all_weapons.get('secondary'); + const all_weapons = doc.equipped.weapons; //They don't have a shield - if (shield == undefined) { + if (all_weapons == undefined) { thread.send("You don't have a shield equipped!"); - return postActionBar(thread, user_dbo); } + const shield = all_weapons.secondary; //Change state user_dbo.updateOne({state: {$exists: true}}, {$set: {state: STATE.DEFENDING}}); - }) - changeTurn(client, bot, interaction); - postActionBar(thread, user_dbo); + changeTurn(client, bot, interaction); + }) } @@ -237,23 +286,43 @@ function cast() { +/** + * + * @param {MongoClient} client + * @param {*} user_dbo + * @param {*} other_dbo + * @param {Client} bot + * @param {*} thread + * @param {String} command + * @param {String} mongouri + * @param {String[]} items + * @param {*} interaction + * @param {Map} xp_collection + */ async function handle(client, user_dbo, other_dbo, bot, thread, command, mongouri, items, interaction, xp_collection) { + if (command == 'initalize') { return postActionBar(thread, user_dbo); } else if (command == 'attack') { attack(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction); postActionBar(thread, other_dbo); } else if (command == 'items') { - presentItems(interaction, client, user_dbo, bot, thread); + presentItems(interaction, client, user_dbo, bot, thread); //Maybe like wands? } else if (command == 'heal') { heal(interaction, client, user_dbo, bot, thread, command, mongouri, items); //.then(() => {postActionBar(thread, other_dbo)}); } else if (command == 'usepotion') { usePotion(interaction, client, user_dbo, bot, thread); } else if (command == 'defend') { defend(client, interaction, user_dbo, bot, thread); + postActionBar(thread, user_dbo); + } else if (command == 'ultimate') { + user_dbo.find({'gamesettings': {$exists: true}}).toArray((err, docs) => { + var gclass = new game_class_battle(docs[0].gamesettings.battle.class); + attack_special(client, user_dbo, other_dbo, bot, thread, xp_collection, interaction, gclass); + postActionBar(thread, user_dbo); + }); } - - // initiate(user_dbo, other_dbo, command, message); } + module.exports = { handle, postActionBar } diff --git a/commands/db/econ.js b/commands/db/econ.js index 3d337f3..00b8e71 100644 --- a/commands/db/econ.js +++ b/commands/db/econ.js @@ -42,7 +42,7 @@ function CreateNewCollection(message, client, server, id, opponent = null, game if (!collinfo) { message.reply("You didn't have a place in my databases, so I created one for you!\nPlease try your command again!") let hp_mp = {maxhp: BASE.HP, hp: BASE.HP, maxmp: BASE.MP, mp: BASE.MP} - dbo.insertOne({balance: 10, rank: 1, lastdayworked: 0, xp: 0, hpmp: hp_mp, game: game, opponent: opponent, state: STATE.IDLE, equipped: { weapons: {main: null, secondary: null}, items: {}}}); + dbo.insertOne({balance: 10, rank: 1, lastdayworked: 0, xp: 0, hpmp: hp_mp, game: game, gamesettings: {battle: {class: 'none', ultimate: true}}, opponent: opponent, state: STATE.IDLE, equipped: { weapons: {main: null, secondary: null}, items: {}}}); } }); }); @@ -98,7 +98,7 @@ function getBalance(dbo, message) { if (doc[0] && doc[0].balance) { bal = doc[0].balance; } - return message.reply(`Your current balance is ${currencySymbol}${bal}`); + return message.reply(`<@${message.author.id}>, your current balance is ${currencySymbol}${bal}`); }); } diff --git a/commands/db/external_game_functions.js b/commands/db/external_game_functions.js index 1ff876c..dcfd254 100644 --- a/commands/db/external_game_functions.js +++ b/commands/db/external_game_functions.js @@ -55,6 +55,8 @@ function winGame(client, bot, db, user_dbo, xp_collection, message) { //Update the player with xp user_dbo.updateOne({"game": {$exists: true}}, { $set: { game: null, opponent: null, state: STATE.IDLE, xp: doc.xp + (BASE.XP * doc.rank), 'hpmp.hp': doc.hpmp.maxhp, 'hpmp.mp': doc.hpmp.maxmp }}); + const channel = bot.channels.cache.get(message.channel.parentId); + channel.send(`<@${user_dbo.s.namespace.collection}> just won a game of "${docs[0].game}"!`); message.channel.delete(); }); } diff --git a/commands/db/game.js b/commands/db/game.js index 6c0d49c..76bbd0c 100644 --- a/commands/db/game.js +++ b/commands/db/game.js @@ -8,6 +8,7 @@ const STATE = ecoimport.STATE; const BASE = ecoimport.BASE; const { winGame, loseGame, equipItem } = require('./external_game_functions.js'); +const { chooseClass, presentClasses } = require('./game_classes.js'); //Has a list of all games (used to change player state) const allGames = ['battle']; @@ -315,6 +316,8 @@ module.exports ={ } else if (command == 'equip') { // equipItem(client, bot, db, dbo, message); equip(message, args, command, dbo, bot, items); + } else if (command == 'classes') { + presentClasses(message, args[1]); } //#endregion diff --git a/commands/db/game_classes.js b/commands/db/game_classes.js new file mode 100644 index 0000000..4750e5f --- /dev/null +++ b/commands/db/game_classes.js @@ -0,0 +1,88 @@ +//Leave this as it's own file in case I want to expand the classes in the future + +const { MessageActionRow, MessageSelectMenu } = require("discord.js"); + + +//#region multiplayer games + +/** + * A temporary container to keep track of what abilities each class has + * @param {string} name - the name of the class ('fighter', 'wizard', etc.) + * @property { Boolean } canUseWeapons + * @property { Boolean } canUseSpells + * @example var myClass = new game_class('wizard'); + */ +class game_class_battle { + constructor(name = 'none') { + if (name == 'fighter') { + this.canUseWeapons = true; + this.canUseSpells = false; + this.specialAttack = { + icon: 'spatkfight', + dmg: 'r*2.5', + prone: false + }; + this.description = 'More damage, less effects!'; + } else if (name == 'wizard') { + this.canUseWeapons = false; + this.canUseSpells = true; + this.specialAttack = { + icon: 'spatkwiz', + dmg: 'r*2.0', + prone: true + } + this.description = 'Less damage, more effects!'; + } else if (name == 'none') { + //The player doesn't have a class + this.canUseSpells = undefined; + this.canUseWeapons = undefined; + this.specialAttack = undefined; + this.description = undefined; + } + + this.className = name; + } +} + +//#endregion + + + + +//#region functions +function presentClasses(message, game) { + let classes; + + if (game == 'battle') { + classes = [new game_class_battle('fighter'), new game_class_battle('wizard')]; + } else { + return message.reply('Please use the following format for this command: _!game class [game name]_'); + } + + var classList = []; + + classes.forEach(function(c) { + let n = c.className; + + classList.push({label: n, description: `${c.description}`, value: `${n}`}); + }); + + const row = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId(`${message.author.id}|class`) + .setPlaceholder('none') + .addOptions(classList) + ) + + message.reply({ content: `Please choose your class <@${message.author.id}>`, components: [row] }); +} + + +function chooseClass(user_dbo, message, game) { + +} + +//#endregion + +module.exports = { game_class_battle, presentClasses, chooseClass } \ No newline at end of file diff --git a/commands/turnManager.js b/commands/turnManager.js index c487410..5b81dac 100644 --- a/commands/turnManager.js +++ b/commands/turnManager.js @@ -1,5 +1,7 @@ //THESE STRUCTURES SUPPORTS TWO PLAYERS ONLY!!!! +const { STATE } = require("./db/econ"); + //Determines who's turn it currently is @@ -38,10 +40,33 @@ function getTurn(client, bot, interaction) { function changeTurn(client, bot, interaction) { const db = client.db('B|S' + bot.user.id); const dbo = db.collection(interaction.member.guild.id); + dbo.find({turn: {$exists: true}}).toArray(function (err, docs) { - let turn = docs[0].turn; - turn = Number(!turn); - dbo.updateOne(docs[0], {$set: {turn: turn}}); + let turnnumer = docs[0].turn; + turnnumer = Number(!turnnumer); + + //Check for prone, and change it if necessary + let turnInfo = getTurn(client, bot, interaction); + + turnInfo.then(id = (turn => { + var id; + // console.log(turn); throw 1; + for (const [key, value] of Object.entries(turn[1])) { + if (key == turnnumer) { id = value; break; } + } + + + const other_dbo = client.db(interaction.member.guild.id + '[ECON]').collection(id); + + other_dbo.find({'state': {$exists: true}}).toArray((err, docs) => { + //If the person was prone, skip their turn + if (docs[0].state == STATE.PRONE) { + dbo.updateOne({'turn': {$exists: true}}, {$set: {state: STATE.FIGHTING}}); + } else { + dbo.updateOne({'turn': {$exists: true}}, {$set: {turn: turnnumer}}); + } + }); + })) }); } diff --git a/main.js b/main.js index 95e98e8..a38b9c4 100644 --- a/main.js +++ b/main.js @@ -164,7 +164,7 @@ bot.on('ready', async () => { bot.on('interactionCreate', async interaction => { if (interaction.isButton()) { - const battlecommandlist = ['ATTACK', 'HEAL', 'DEFEND', 'ITEMS']; + const battlecommandlist = ['ATTACK', 'HEAL', 'DEFEND', 'ITEMS', 'ULTIMATE']; const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 }); client.connect(err => { @@ -183,7 +183,7 @@ bot.on('interactionCreate', async interaction => { await interaction.deferReply(); //Check State - if (docs[0].state == STATE.FIGHTING) { + if (docs[0].state != STATE.IDLE) { //Do turn stuff bot.commands.get('game').in_game_redirector(bot, interaction, threadname, doc, client, mongouri, items, xp_collection); } @@ -204,12 +204,17 @@ bot.on('interactionCreate', async interaction => { client.close(); } + + //Menu Selection else if (interaction.isSelectMenu()) { + const id = interaction.customId.substring(0, interaction.customId.indexOf('|')) + const command = interaction.customId.substring(interaction.customId.indexOf('|'), interaction.customId.length - interaction.customId.indexOf('|')) + console.log(command); + if (interaction.customId.toLowerCase().indexOf('|heal') != -1) { const client = new MongoClient(mongouri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 }); client.connect(err => { - const id = interaction.customId.substring(0, interaction.customId.indexOf('|')) - + console.log(id); if (id != interaction.user.id) { return; } let current_user = turnManager.getTurn(client, bot, interaction); @@ -228,20 +233,6 @@ bot.on('interactionCreate', async interaction => { //Do turn stuff bot.commands.get('game').in_game_redirector(bot, interaction, threadname, doc, client, mongouri, items, xp_collection); } - - /* - let srv = bot.guilds.cache.get(bot.home_server).emojis.cache; - let sname; - - if (interaction.customId.toLowerCase() == 'heal' || interaction.customId.toLowerCase() == 'mp') { - if (interaction.values[0] == 'HP Potion') { sname = 'healing_potion' } - else if (interaction.values[0] == 'MP Potion') { sname = 'mana_potion' } - else if (interaction.values[0] == 'Super HP Potion') { sname = 'superior_healing_potion' } - else if (interaction.values[0] == 'Super MP Potion') { sname = 'superior_mana_potion' } - } - - // emj = srv.find((g) => { return g.name == sname }); - // console.log(sname, srv);*/ interaction.editReply(`<@${interaction.user.id}> used a _${interaction.values[0]}_!`); @@ -257,8 +248,12 @@ bot.on('interactionCreate', async interaction => { //Get all chars from after "CUSTOM|" to the end of the str // let name = item.icon.substr(7, item.icon.length - 6); }); + } else if (interaction.customId.toLowerCase().indexOf('|item') != -1) { + } - } + + //menu else ifs here + } //other selection types here }); @@ -305,19 +300,6 @@ bot.on('messageCreate', (message) => { const command = args.shift().toLowerCase(); - if (command == 'welcome') { - const row = new MessageActionRow() - .addComponents( - new MessageButton() - .setCustomId('WELCOME') - .setLabel('WELCOME') - .setStyle('PRIMARY') - ); - - message.channel.send({ components: [row] }); - } - - //Performes the command //Admin section if (command == 'reactionrole') { bot.commands.get(command).execute(message, args, Discord, bot); } diff --git a/package-lock.json b/package-lock.json index 95d3b7f..cbb5157 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3090,9 +3090,9 @@ "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==" }, "node_modules/undici": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.4.0.tgz", - "integrity": "sha512-A1SRXysDg7J+mVP46jF+9cKANw0kptqSFZ8tGyL+HBiv0K1spjxPX8Z4EGu+Eu6pjClJUBdnUPlxrOafR668/g==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", + "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==", "engines": { "node": ">=12.18" } @@ -5674,9 +5674,9 @@ "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==" }, "undici": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.4.0.tgz", - "integrity": "sha512-A1SRXysDg7J+mVP46jF+9cKANw0kptqSFZ8tGyL+HBiv0K1spjxPX8Z4EGu+Eu6pjClJUBdnUPlxrOafR668/g==" + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", + "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==" }, "unique-filename": { "version": "1.1.1",