Skip to content

Commit

Permalink
Solo nex (#5770)
Browse files Browse the repository at this point in the history
  • Loading branch information
gc authored Mar 10, 2024
1 parent c0dbe7a commit 1326c96
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 46 deletions.
3 changes: 3 additions & 0 deletions src/lib/simulation/nex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion src/lib/util/repeatStoredTrip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ export const tripHandlers = {
args: (data: NexTaskOptions) => {
return {
name: 'nex',
quantity: data.quantity
quantity: data.quantity,
solo: data.userDetails.length === 1
};
}
},
Expand Down
5 changes: 3 additions & 2 deletions src/lib/util/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -24,7 +25,7 @@ export async function resolveChannel(channelID: string): Promise<WebhookClient |
return new WebhookClient({ id: db.webhook_id, token: db.webhook_token });
}

if (!channel.permissionsFor(globalClient.user!)?.has(PermissionsBitField.Flags.ManageWebhooks)) {
if (!production || !channel.permissionsFor(globalClient.user!)?.has(PermissionsBitField.Flags.ManageWebhooks)) {
return channel;
}

Expand Down Expand Up @@ -107,7 +108,7 @@ export async function sendToChannelID(
});
}
}
queue.add(queuedFn);
return queue.add(queuedFn);
}

async function sendToChannelOrWebhook(channel: WebhookClient | Message['channel'], input: BaseMessageOptions) {
Expand Down
24 changes: 22 additions & 2 deletions src/mahoji/commands/k.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,38 @@ export const killCommand: OSBMahojiCommand = {
name: 'show_info',
description: 'Show information on this monster.',
required: false
},
{
type: ApplicationCommandOptionType.Boolean,
name: 'solo',
description: 'Solo (if its a group boss)',
required: false
}
],
run: async ({
options,
userID,
channelID,
interaction
}: CommandRunOptions<{ name: string; quantity?: number; method?: PvMMethod; show_info?: boolean }>) => {
}: 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
);
}
};
5 changes: 3 additions & 2 deletions src/mahoji/lib/abstracted_commands/minionKill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.';
Expand All @@ -147,7 +148,7 @@ export async function minionKillCommand(

if (!name) return invalidMonsterMsg;

Check warning on line 149 in src/mahoji/lib/abstracted_commands/minionKill.ts

View workflow job for this annotation

GitHub Actions / Node v18.12.0 - ubuntu-latest

Unexpected string value in conditional. An explicit empty string check is required

Check warning on line 149 in src/mahoji/lib/abstracted_commands/minionKill.ts

View workflow job for this annotation

GitHub Actions / Node v20 - ubuntu-latest

Unexpected string value in conditional. An explicit empty string check is required

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);
Expand Down
72 changes: 43 additions & 29 deletions src/mahoji/lib/abstracted_commands/nexCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.';

Expand All @@ -22,50 +27,59 @@ 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]) {
return result[1];
}
}

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}.`;
}
}

const removeResult = await Promise.all(
details.team.map(async i => {
effectiveTeam.map(async i => {
const klasaUser = await mUserFetch(i.id);
return {
id: klasaUser.id,
Expand Down Expand Up @@ -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
Expand Down
35 changes: 25 additions & 10 deletions src/tasks/minions/nexActivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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()) {
Expand All @@ -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()
);
Expand Down

0 comments on commit 1326c96

Please sign in to comment.