From d2dbb53d69d4c599d1f5ccad7a15538c3b1f2cf6 Mon Sep 17 00:00:00 2001 From: DaInfLoop Date: Mon, 24 Jun 2024 20:56:44 +0100 Subject: [PATCH] feat: downgrading + special opponents! battler is you x2 --- index.js | 326 +++++++++++++++++++++++++++++++++++++------ opponents/special.js | 29 ++++ 2 files changed, 314 insertions(+), 41 deletions(-) create mode 100644 opponents/special.js diff --git a/index.js b/index.js index d1aab02..c86bda3 100644 --- a/index.js +++ b/index.js @@ -8,10 +8,12 @@ const sql = postgres({ username: 'haroon' }) +const SpecialOpponents = require('./opponents/special'); const BeginnerOpponents = require('./opponents/beginner'); const AllOpponents = [ - ...BeginnerOpponents + ...(SpecialOpponents.map(x => ({ ...x, rank: 'SPECIAL' }))), + ...(BeginnerOpponents.map(x => ({ ...x, rank: 'BEGINNER' }))), ] const app = new App({ @@ -181,7 +183,7 @@ function generateProfile(dbUser, slackUser) { { "type": "mrkdwn", "text": `*Creation Shards:* ${dbUser.cshards}\n*Destruction Shards:* ${dbUser.dshards}\n*Skill Points:* ${dbUser.spoints}` - } + } ] } ] @@ -309,16 +311,24 @@ app.view("chooseopponent", async (ctx) => { "type": "input", "element": { "type": "static_select", - "options": BeginnerOpponents.map(opponent => - ({ - "text": { - "type": "plain_text", - "text": `${opponent.name} // ${opponent.stats.health + opponent.stats.min + opponent.stats.max} Battle Power`, - "emoji": true - }, - "value": opponent.rawId - }) - ), + "options": AllOpponents.filter(op => op.rank == rank).map(opponent => { + const battlePower = ((opponent) => { + if (opponent.stats.health instanceof Function) { + return opponent.stats.health(user) + opponent.stats.min(user) + opponent.stats.max(user) + } else { + return opponent.stats.health + opponent.stats.min + opponent.stats.max + } + })(opponent); + + return { + "text": { + "type": "plain_text", + "text": `${opponent.name} // ${battlePower} Battle Power`, + "emoji": true + }, + "value": opponent.rawId + } + }), "action_id": "opponents" }, "label": { @@ -327,7 +337,7 @@ app.view("chooseopponent", async (ctx) => { "emoji": true } }, - ...( canRankUp ? [ + ...(canRankUp ? [ { type: 'divider', }, @@ -417,6 +427,69 @@ app.view("chooseopponent-BEGINNER", async (ctx) => { await sql`UPDATE users SET battlemessage = ${`https://hackclub.slack.com/archives/${channelId}/p${msg.ts.replace('.', '')}`} WHERE slack_id = ${userId};` }) +app.view("chooseopponent-SPECIAL", async (ctx) => { + await ctx.ack(); + + const channelId = ctx.view.private_metadata; + const userId = ctx.context.userId; + + const slackUser = (await ctx.client.users.info({ user: userId })).user.profile; + + const opponent = SpecialOpponents.find(o => o.rawId == Object.values(ctx.payload.state.values)[0].opponents.selected_option.value); + + const player = await initializeUser(userId); + + await sql`UPDATE users + SET playerhealth = ${player.health}, + playermin = ${player.mindmg}, + playermax = ${player.maxdmg}, + + currentOpponent = ${opponent.rawId}, + + opponenthealth = ${opponent.stats.health(player)}, + opponentmin = ${opponent.stats.min(player)}, + opponentmax = ${opponent.stats.max(player)} + + WHERE slack_id = ${userId};` + + const msg = await ctx.client.chat.postMessage({ + channel: channelId, + text: `${slackUser.display_name_normalized} started a battle against ${opponent.name}.`, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: opponent.intro.replaceAll("{player}", slackUser.display_name_normalized) + }, + "accessory": { + "type": "image", + "image_url": opponent.image, + "alt_text": opponent.name + } + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + + "text": { + "type": "plain_text", + "text": "Continue", + "emoji": true + }, + "value": userId, + "action_id": "continue" + } + ] + } + ] + }); + + await sql`UPDATE users SET battlemessage = ${`https://hackclub.slack.com/archives/${channelId}/p${msg.ts.replace('.', '')}`} WHERE slack_id = ${userId};` +}) + async function checkButton(ctx) { await ctx.ack(); @@ -486,7 +559,7 @@ async function playerLoss(ctx) { UPDATE users SET spoints = ${user.spoints - 1}, cshards = ${user.cshards - 5}, - defeats = ${user.defeats + 1} + losses = ${user.losses + 1} WHERE slack_id = ${ctx.context.userId}; ` @@ -832,20 +905,20 @@ app.action('viewaction-opponent', checkButton, async (ctx) => { response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`NO DAMAGE\`\`\`` } else { const damage = Math.floor(Math.random() * (user.opponentmax - user.opponentmin + 1)) + user.opponentmin; - await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};` + await sql`UPDATE users SET playerhealth = ${user.playerhealth - damage} WHERE slack_id = ${ctx.context.userId};` response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\`` } } else if (user.playerdefense == 'Weak') { if (Math.random() < 0.25) { response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${lackUser.display_name_normalized}_*\n\n\`\`\`NO DAMAGE\`\`\`` } else { - const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin; - await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};` + const damage = Math.floor(Math.random() * (user.opponentmax - user.opponentmin + 1)) + user.opponentmin; + await sql`UPDATE users SET playerhealth = ${user.playerhealth - damage} WHERE slack_id = ${ctx.context.userId};` response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\`` } } else { - const damage = Math.floor(Math.random() * (user.playermax - user.playermin + 1)) + user.playermin; - await sql`UPDATE users SET opponenthealth = ${user.opponenthealth - damage} WHERE slack_id = ${ctx.context.userId};` + const damage = Math.floor(Math.random() * (user.opponentmax - user.opponentmin + 1)) + user.opponentmin; + await sql`UPDATE users SET playerhealth = ${user.playerhealth - damage} WHERE slack_id = ${ctx.context.userId};` response = `*_${AllOpponents.find(x => x.rawId == user.currentopponent).name} attacks ${slackUser.display_name_normalized}_*\n\n\`\`\`${damage.toLocaleString()} DAMAGE\`\`\`` } break; @@ -993,17 +1066,17 @@ function getTimeDifference(date1, date2) { // Ensure both dates are Date objects const start = new Date(date1); const end = new Date(date2); - + // Calculate the difference in milliseconds const diff = Math.abs(end - start); - + // Calculate the difference in weeks, days, hours, minutes, and seconds const weeks = Math.floor(diff / (1000 * 60 * 60 * 24 * 7)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)) % 7; const hours = Math.floor(diff / (1000 * 60 * 60)) % 24; const minutes = Math.floor(diff / (1000 * 60)) % 60; const seconds = Math.floor(diff / 1000) % 60; - + // Determine which unit to use for the output if (weeks > 0) { return `${weeks} Week${weeks > 1 ? 's' : ''}`; @@ -1020,7 +1093,7 @@ function getTimeDifference(date1, date2) { app.command('/daily', async (ctx) => { await ctx.ack(); - const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` + const [cooldown] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` const user = await initializeUser(ctx.context.userId) const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile; @@ -1029,7 +1102,7 @@ app.command('/daily', async (ctx) => { if (!cooldown.daily || (cooldown.daily.getTime() <= now.getTime())) { const cshards = Math.floor(Math.random() * 10) + 1; const spoints = Math.floor(Math.random() * 5) + 1; - + ctx.respond({ response_type: 'ephemeral', @@ -1042,8 +1115,8 @@ app.command('/daily', async (ctx) => { } } ] - }) - + }) + await sql`UPDATE users SET cshards = ${user.cshards + cshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};` await sql`UPDATE cooldowns SET daily = ${new Date(now.getTime() + (24 * 60 * 60 * 1000))};` @@ -1065,9 +1138,9 @@ app.command('/daily', async (ctx) => { app.command('/weekly', async (ctx) => { await ctx.ack(); - const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` + const [cooldown] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` const user = await initializeUser(ctx.context.userId) - const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile; + const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile; const now = new Date(); @@ -1087,11 +1160,11 @@ app.command('/weekly', async (ctx) => { } } ] - }) - + }) + await sql`UPDATE users SET cshards = ${user.cshards + cshards}, dshards = ${user.dshards + dshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};` - await sql`UPDATE cooldowns SET weekly = ${new Date(now.getTime() + (7 * 24 * 60 * 60 * 1000))};` + await sql`UPDATE cooldowns SET weekly = ${new Date(now.getTime() + (7 * 24 * 60 * 60 * 1000))};` } else { ctx.respond({ response_type: 'ephemeral', @@ -1110,7 +1183,7 @@ app.command('/weekly', async (ctx) => { app.command('/monthly', async (ctx) => { await ctx.ack(); - const [ cooldown ] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` + const [cooldown] = await sql`SELECT * FROM cooldowns WHERE slack_id = ${ctx.context.userId};` const user = await initializeUser(ctx.context.userId) const slackUser = (await ctx.client.users.info({ user: ctx.context.userId })).user.profile; @@ -1132,11 +1205,11 @@ app.command('/monthly', async (ctx) => { } } ] - }) - + }) + await sql`UPDATE users SET cshards = ${user.cshards + cshards}, dshards = ${user.dshards + dshards}, spoints = ${user.spoints + spoints} WHERE slack_id = ${ctx.context.userId};` - await sql`UPDATE cooldowns SET monthly = ${new Date(now.getTime() + (30 * 24 * 60 * 60 * 1000))};` + await sql`UPDATE cooldowns SET monthly = ${new Date(now.getTime() + (30 * 24 * 60 * 60 * 1000))};` } else { ctx.respond({ response_type: 'ephemeral', @@ -1261,7 +1334,7 @@ app.view('upgrade', async (ctx) => { errors: { 'select': "You need to upgrade your maximum damage first!" } - }); + }); } else if (selected_option.value == "maxdmg" && user.spoints < 15) { return await ctx.ack({ response_action: 'errors', @@ -1270,7 +1343,7 @@ app.view('upgrade', async (ctx) => { } }); } - + await ctx.ack(); if (selected_option.value == "health") { @@ -1324,8 +1397,179 @@ app.view('upgrade', async (ctx) => { } }) -; (async () => { - await app.start(process.env.PORT); +app.command('/downgrade', async (ctx) => { + await ctx.ack(); - console.log('⚡️ Bolt app is running!'); -})(); \ No newline at end of file + const user = await initializeUser(ctx.context.userId); + + await ctx.client.views.open({ + trigger_id: ctx.body.trigger_id, + view: { + "private_metadata": ctx.payload.channel_id, + "type": "modal", + "callback_id": "downgrade", + "title": { + "type": "plain_text", + "text": "Downgrade your stats", + "emoji": true + }, + "submit": { + "type": "plain_text", + "text": "Downgrade", + "emoji": true + }, + "close": { + "type": "plain_text", + "text": "Never mind", + "emoji": true + }, + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": `*Battle Builder:* Helloooo <@${ctx.context.userId}>! You're downgrading today huh? No problem! What would you like to... Erm... Downgrade?-` + } + }, + { + "block_id": "select", + "type": "input", + "element": { + "type": "static_select", + "placeholder": { + "type": "plain_text", + "text": "Choose a statistic...", + "emoji": true + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "Health // 30 Creation Shards" + (user.cshards < 30 ? " :lock:" : ""), + "emoji": true + }, + "value": "health" + }, + { + "text": { + "type": "plain_text", + "text": "Min Damage // 40 Creation Shards" + (user.cshards < 40 ? " :lock:" : ""), + "emoji": true + }, + "value": "mindmg" + }, + { + "text": { + "type": "plain_text", + "text": "Max Damage // 50 Creation Shards" + (user.cshards < 50 || user.mindmg + 5 == user.maxdmg ? " :lock:" : ""), + "emoji": true + }, + "value": "maxdmg" + } + ], + "action_id": "downgrade-modal" + }, + "label": { + "type": "plain_text", + "text": "What would you like to downgrade?", + "emoji": true + } + } + ] + } + }) +}); + +app.view('downgrade', async (ctx) => { + const user = await initializeUser(ctx.context.userId); + + const { selected_option } = ctx.view.state.values['select']['downgrade-modal']; + + if (selected_option.value == "health" && user.cshards < 30) { + return await ctx.ack({ + response_action: 'errors', + errors: { + 'select': "You don't have enough creation shards to downgrade your health!" + } + }); + } else if (selected_option.value == "mindmg" && user.cshards < 40) { + return await ctx.ack({ + response_action: 'errors', + errors: { + 'select': "You don't have enough creation shards to downgrade your minimum damage!" + } + }); + } else if (selected_option == "maxdmg" && user.mindmg + 5 == user.maxdmg) { + return await ctx.ack({ + response_action: 'errors', + errors: { + 'select': "You need to downgrade your minimum damage first!" + } + }); + } else if (selected_option.value == "maxdmg" && user.cshards < 50) { + return await ctx.ack({ + response_action: 'errors', + errors: { + 'select': "You don't have enough creation shards to downgrade your maximum damage!" + } + }); + } + + await ctx.ack(); + + if (selected_option.value == "health") { + await sql`UPDATE users SET spoints = ${user.spoints + 5}, health = ${user.health - 1}, cshards = ${user.cshards - 30} WHERE slack_id = ${ctx.context.userId};` + + await ctx.client.chat.postEphemeral({ + channel: ctx.view.private_metadata, + user: ctx.context.userId, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `*Battle Builder:* Yayyy <@${ctx.context.userId}>! Your health has been successfully decreased!\n\n\`\`\`HEALTH DECREASED (${user.health} > ${user.health - 1})\`\`\`` + } + } + ] + }) + } else if (selected_option.value == "mindmg") { + await sql`UPDATE users SET spoints = ${user.spoints + 10}, mindmg = ${user.mindmg - 1}, cshards = ${user.cshards - 40} WHERE slack_id = ${ctx.context.userId};` + + await ctx.client.chat.postEphemeral({ + channel: ctx.view.private_metadata, + user: ctx.context.userId, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `*Battle Builder:* Yayyy <@${ctx.context.userId}>! Your minimum damage has been successfully decreased!\n\n\`\`\`MIN DAMAGE DECREASED (${user.mindmg} > ${user.mindmg - 1})\`\`\`` + } + } + ] + }) + } else if (selected_option.value == "maxdmg") { + await sql`UPDATE users SET spoints = ${user.spoints + 15}, maxdmg = ${user.maxdmg - 1}, cshards = ${user.cshards - 50} WHERE slack_id = ${ctx.context.userId};` + + await ctx.client.chat.postEphemeral({ + channel: ctx.view.private_metadata, + user: ctx.context.userId, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `*Battle Builder:* Yayyy <@${ctx.context.userId}>! Your maximum damage has been successfully decreased!\n\n\`\`\`MAX DAMAGE DECREASED (${user.maxdmg} > ${user.maxdmg - 1})\`\`\`` + } + } + ] + }) + } +}) + + ; (async () => { + await app.start(process.env.PORT); + + console.log('⚡️ Bolt app is running!'); + })(); \ No newline at end of file diff --git a/opponents/special.js b/opponents/special.js new file mode 100644 index 0000000..c47ce52 --- /dev/null +++ b/opponents/special.js @@ -0,0 +1,29 @@ +module.exports = [ + { + "rawId": "battler", + "name": "Battler", + "stats": { + health: (user) => user.health * 2, + min: (user) => user.mindmg * 2, + max: (user) => user.maxdmg * 2 + }, + "image": "https://cdn.discordapp.com/attachments/1108683335347212389/1118840155109851167/Battler_Closeup.png?ex=667a9318&is=66794198&hm=4ee29c3ef2691c56a92ae77744be00b5526fbb271788b6afa1eabd268beaf23a&", + "intro": `*_A glowing white portal appears as Battler dramatically storms out. He is heading directly for {player}_* + +*Battler*: Greetings {player}. I would like to assume you are looking for an opponent, are you not? +*{player}*: How do you know my name... And who are you!? +*Battler*: I am you, but better. +*{player}*: What? Are you from the future or something? +*Battler*: Precisely. I am you, but stronger. +*{player}*: Wait but if you're me from the future then how am I supposed to beat you!? +*Battler*: Battler's Mentality #8. Never give up. +*{player}*: What does that even mean!? +*Battler*: Good luck {player}. However, don't feel bad if you are defeated. After all, I am you... X2. +*{player}*: ...`, + "chances": { + "attack": 0.65, + "defend": 0.175, + "item": 0.175 + } + } +] \ No newline at end of file