diff --git a/src/lib/simulation/nex.ts b/src/lib/simulation/nex.ts index 92a6e28dd3..ebeaaf89d7 100644 --- a/src/lib/simulation/nex.ts +++ b/src/lib/simulation/nex.ts @@ -143,6 +143,9 @@ export function handleNexKills({ quantity, team }: NexContext) { for (let i = 0; i < quantity; i++) { const survivors = team.filter(usr => !usr.deaths.includes(i)); + if (survivors.length === 0) { + continue; + } const uniqueRecipient = roll(43) ? randArrItem(survivors).id : null; const nonUniqueDrop = NexNonUniqueTable.roll(); diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index 2e98b5f663..6d30f31d2b 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -406,7 +406,8 @@ export const tripHandlers = { args: (data: NexTaskOptions) => { return { name: 'nex', - quantity: data.quantity + quantity: data.quantity, + solo: data.userDetails.length === 1 }; } }, diff --git a/src/lib/util/webhook.ts b/src/lib/util/webhook.ts index 7fb32ffd3d..311194fb48 100644 --- a/src/lib/util/webhook.ts +++ b/src/lib/util/webhook.ts @@ -10,6 +10,7 @@ import { } from 'discord.js'; import PQueue from 'p-queue'; +import { production } from '../../config'; import { prisma } from '../settings/prisma'; import { channelIsSendable } from '../util'; import { logError } from './logError'; @@ -24,7 +25,7 @@ export async function resolveChannel(channelID: string): Promise) => { + }: CommandRunOptions<{ + name: string; + quantity?: number; + method?: PvMMethod; + show_info?: boolean; + solo?: boolean; + }>) => { const user = await mUserFetch(userID); if (options.show_info) { return returnStringOrFile(await monsterInfo(user, options.name)); } - return minionKillCommand(user, interaction, channelID, options.name, options.quantity, options.method); + return minionKillCommand( + user, + interaction, + channelID, + options.name, + options.quantity, + options.method, + options.solo + ); } }; diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index a7166489b3..457306c94e 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -131,7 +131,8 @@ export async function minionKillCommand( channelID: string, name: string, quantity: number | undefined, - method: PvMMethod | undefined + method: PvMMethod | undefined, + solo: boolean | undefined ) { if (user.minionIsBusy) { return 'Your minion is busy.'; @@ -147,7 +148,7 @@ export async function minionKillCommand( if (!name) return invalidMonsterMsg; - if (stringMatches(name, 'nex')) return nexCommand(interaction, user, channelID); + if (stringMatches(name, 'nex')) return nexCommand(interaction, user, channelID, solo); if (stringMatches(name, 'zalcano')) return zalcanoCommand(user, channelID); if (stringMatches(name, 'tempoross')) return temporossCommand(user, channelID, quantity); if (name.toLowerCase().includes('nightmare')) return nightmareCommand(user, channelID, name, quantity); diff --git a/src/mahoji/lib/abstracted_commands/nexCommand.ts b/src/mahoji/lib/abstracted_commands/nexCommand.ts index 503c6fc574..22d78bea7e 100644 --- a/src/mahoji/lib/abstracted_commands/nexCommand.ts +++ b/src/mahoji/lib/abstracted_commands/nexCommand.ts @@ -11,7 +11,12 @@ import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask import { deferInteraction } from '../../../lib/util/interactionReply'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; -export async function nexCommand(interaction: ChatInputCommandInteraction, user: MUser, channelID: string) { +export async function nexCommand( + interaction: ChatInputCommandInteraction, + user: MUser, + channelID: string, + solo: boolean | undefined +) { const channel = globalClient.channels.cache.get(channelID.toString()); if (!channel || channel.type !== ChannelType.GuildText) return 'You need to run this in a text channel.'; @@ -22,30 +27,35 @@ export async function nexCommand(interaction: ChatInputCommandInteraction, user: await deferInteraction(interaction); - let usersWhoConfirmed: MUser[] = []; - try { - usersWhoConfirmed = await setupParty(channel as TextChannel, user, { - minSize: 2, - maxSize: 10, - leader: user, - ironmanAllowed: true, - message: `${user} is hosting a Nex mass! Use the buttons below to join/leave.`, - customDenier: async user => checkNexUser(await mUserFetch(user.id)) - }); - } catch (err: any) { - return { - content: typeof err === 'string' ? err : 'Your mass failed to start.', - ephemeral: true - }; - } - usersWhoConfirmed = usersWhoConfirmed.filter(i => !i.minionIsBusy); + let mahojiUsers: MUser[] = []; + + if (solo) { + mahojiUsers = [user]; + } else { + let usersWhoConfirmed: MUser[] = []; + try { + usersWhoConfirmed = await setupParty(channel as TextChannel, user, { + minSize: 1, + maxSize: 10, + leader: user, + ironmanAllowed: true, + message: `${user} is hosting a Nex mass! Use the buttons below to join/leave.`, + customDenier: async user => checkNexUser(await mUserFetch(user.id)) + }); + } catch (err: any) { + return { + content: typeof err === 'string' ? err : 'Your mass failed to start.', + ephemeral: true + }; + } + usersWhoConfirmed = usersWhoConfirmed.filter(i => !i.minionIsBusy); - if (usersWhoConfirmed.length < 2 || usersWhoConfirmed.length > 10) { - return `${user}, your mass didn't start because it needs atleast 2 users.`; + if (usersWhoConfirmed.length < 1 || usersWhoConfirmed.length > 10) { + return `${user}, your mass didn't start because it needs between 1-10 users.`; + } + mahojiUsers = await Promise.all(usersWhoConfirmed.map(i => mUserFetch(i.id))); } - const mahojiUsers = await Promise.all(usersWhoConfirmed.map(i => mUserFetch(i.id))); - for (const user of mahojiUsers) { const result = checkNexUser(user); if (result[1]) { @@ -53,11 +63,15 @@ export async function nexCommand(interaction: ChatInputCommandInteraction, user: } } + const isSoloing = mahojiUsers.length === 1; + const details = await calculateNexDetails({ - team: mahojiUsers + team: isSoloing ? [mahojiUsers[0], mahojiUsers[0], mahojiUsers[0], mahojiUsers[0]] : mahojiUsers }); - for (const user of details.team) { + const effectiveTeam = isSoloing ? [details.team[0]] : details.team; + + for (const user of effectiveTeam) { const mUser = await mUserFetch(user.id); if (!mUser.allItemsOwned.has(user.cost)) { return `${mUser.usernameOrMention} doesn't have the required items: ${user.cost}.`; @@ -65,7 +79,7 @@ export async function nexCommand(interaction: ChatInputCommandInteraction, user: } const removeResult = await Promise.all( - details.team.map(async i => { + effectiveTeam.map(async i => { const klasaUser = await mUserFetch(i.id); return { id: klasaUser.id, @@ -97,21 +111,21 @@ export async function nexCommand(interaction: ChatInputCommandInteraction, user: duration: details.duration, type: 'Nex', leader: user.id, - users: details.team.map(i => i.id), - userDetails: details.team.map(i => [i.id, i.contribution, i.deaths]), + users: effectiveTeam.map(i => i.id), + userDetails: effectiveTeam.map(i => [i.id, i.contribution, i.deaths]), fakeDuration: details.fakeDuration, quantity: details.quantity, wipedKill: details.wipedKill }); - let str = `${user.usernameOrMention}'s party (${usersWhoConfirmed + let str = `${user.usernameOrMention}'s party (${mahojiUsers .map(u => u.usernameOrMention) .join(', ')}) is now off to kill ${details.quantity}x Nex! (${calcPerHour( details.quantity, details.fakeDuration ).toFixed(1)}/hr) - the total trip will take ${formatDuration(details.fakeDuration)}. -${details.team +${effectiveTeam .map(i => { return `${userMention(i.id)}: Contrib[${i.contribution.toFixed(2)}%] Death[${i.deathChance.toFixed( 2 diff --git a/src/tasks/minions/nexActivity.ts b/src/tasks/minions/nexActivity.ts index 3e9399e7d4..4ad23a11f8 100644 --- a/src/tasks/minions/nexActivity.ts +++ b/src/tasks/minions/nexActivity.ts @@ -6,6 +6,7 @@ import { trackLoot } from '../../lib/lootTrack'; import { handleNexKills } from '../../lib/simulation/nex'; import { NexTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; +import { makeBankImage } from '../../lib/util/makeBankImage'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; export const nexTask: MinionTask = { @@ -16,13 +17,15 @@ export const nexTask: MinionTask = { const allMUsers = await Promise.all(users.map(id => mUserFetch(id))); const survivedQuantity = wipedKill ? wipedKill - 1 : quantity; + const teamResult = userDetails.map(u => ({ + id: u[0], + contribution: u[1], + deaths: u[2] + })); + const loot = handleNexKills({ quantity: survivedQuantity, - team: userDetails.map(u => ({ - id: u[0], - contribution: u[1], - deaths: u[2] - })) + team: teamResult }); for (const [uID, uLoot] of loot.entries()) { @@ -46,17 +49,29 @@ export const nexTask: MinionTask = { }); await updateBankSetting('nex_loot', loot.totalLoot()); - handleTripFinish( + return handleTripFinish( allMUsers[0], channelID, { - content: `${allMention} Your team finished killing ${quantity}x Nex.${ - wipedKill ? ` Your team wiped on the ${formatOrdinal(wipedKill)} kill.` : '' - } + content: + survivedQuantity === 0 + ? `${allMention} your minion${users.length === 1 ? '' : 's'} died in all kill attempts.` + : `${allMention} Your team finished killing ${quantity}x Nex.${ + wipedKill ? ` Your team wiped on the ${formatOrdinal(wipedKill)} kill.` : '' + } ${loot.formatLoot()}` }, - undefined, + users.length === 1 && loot.totalLoot().length > 0 + ? ( + await makeBankImage({ + bank: loot.totalLoot(), + title: `Loot From ${survivedQuantity}x Nex`, + user: allMUsers[0], + previousCL: undefined + }) + ).file.attachment + : undefined, data, loot.totalLoot() );