From 1fc6c436114b11c7bca75b25dee6970397798140 Mon Sep 17 00:00:00 2001 From: Hri7566 Date: Sat, 24 Feb 2024 11:12:23 -0500 Subject: [PATCH] =?UTF-8?q?Implement=20autofishing=20and=20Pok=C3=A9dex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/convertpok.ts | 2 +- src/api/api/trpc.ts | 14 +++- src/api/commands/groups/fishing/fish.ts | 10 +-- src/api/commands/groups/fishing/go.ts | 9 ++- src/api/commands/groups/fishing/nearby.ts | 2 +- src/api/commands/groups/fishing/reel.ts | 4 +- src/api/commands/groups/general/color.ts | 24 +++++++ src/api/commands/groups/index.ts | 10 ++- src/api/commands/groups/inventory/eat.ts | 43 ++++++------ .../commands/groups/inventory/inventory.ts | 67 +++++++++++++++---- src/api/commands/groups/inventory/pokemon.ts | 28 ++++++++ src/api/commands/groups/inventory/take.ts | 3 +- src/api/commands/groups/util/autofish.ts | 30 +++++++++ src/api/commands/groups/util/pokedex.ts | 30 +++++++++ src/api/commands/handler.ts | 6 +- src/api/fish/fishers.ts | 60 ++++++++++------- src/api/pokemon/pokedex.ts | 10 +++ src/mpp/bot/Bot.ts | 12 +++- src/util/types.d.ts | 12 ++++ 19 files changed, 296 insertions(+), 80 deletions(-) create mode 100644 src/api/commands/groups/general/color.ts create mode 100644 src/api/commands/groups/inventory/pokemon.ts create mode 100644 src/api/commands/groups/util/autofish.ts create mode 100644 src/api/commands/groups/util/pokedex.ts create mode 100644 src/api/pokemon/pokedex.ts diff --git a/scripts/convertpok.ts b/scripts/convertpok.ts index d2433be..470e3e9 100644 --- a/scripts/convertpok.ts +++ b/scripts/convertpok.ts @@ -3,7 +3,7 @@ import { argv } from "bun"; import { existsSync, readFileSync, writeFileSync } from "fs"; import YAML from "yaml"; -const logger = new Logger("Pokemon Converter"); +const logger = new Logger("Pokémon Converter"); const inFile = argv[2]; const outFile = argv[3]; diff --git a/src/api/api/trpc.ts b/src/api/api/trpc.ts index cd88565..b4a9275 100644 --- a/src/api/api/trpc.ts +++ b/src/api/api/trpc.ts @@ -62,13 +62,21 @@ export const appRouter = router({ id: z.string(), name: z.string(), color: z.string() - }) + }), + isDM: z.boolean().optional() }) ) .query(async opts => { const id = tokenToID(opts.ctx.token); - const { command, args, prefix, user } = opts.input; - const out = await handleCommand(id, command, args, prefix, user); + const { command, args, prefix, user, isDM } = opts.input; + const out = await handleCommand( + id, + command, + args, + prefix, + user, + isDM + ); return out; }), diff --git a/src/api/commands/groups/fishing/fish.ts b/src/api/commands/groups/fishing/fish.ts index 59e5f23..d81a686 100644 --- a/src/api/commands/groups/fishing/fish.ts +++ b/src/api/commands/groups/fishing/fish.ts @@ -3,22 +3,24 @@ import { getFishing, startFishing } from "@server/fish/fishers"; export const fish = new Command( "fish", - ["fish", "fosh", "cast"], + ["fish", "fosh", "cast", "startfishing"], "Send your LURE into a water for catching fish", "fish", "command.fishing.fish", - async ({ id, command, args, prefix, part, user }) => { + async ({ id, command, args, prefix, part, user, isDM }) => { const fishing = getFishing(id, part.id); if (!fishing) { - startFishing(id, part.id); + startFishing(id, part.id, isDM); return `Our friend ${part.name} casts LURE into a water for catching fish.`; } else { return `Your lure is already in the water (since ${( (Date.now() - fishing.t) / 1000 / 60 - ).toFixed(2)} minutes ago).`; + ).toFixed(2)} minutes ago).${ + fishing.autofish ? ` (AUTOFISH is enabled)` : `` + }`; } } ); diff --git a/src/api/commands/groups/fishing/go.ts b/src/api/commands/groups/fishing/go.ts index 980c070..29459d9 100644 --- a/src/api/commands/groups/fishing/go.ts +++ b/src/api/commands/groups/fishing/go.ts @@ -2,6 +2,8 @@ import Command from "@server/commands/Command"; import { getInventory, updateInventory } from "@server/data/inventory"; import { locations } from "@server/fish/locations"; import { nearby } from "./nearby"; +import { getFishing, stopFishing } from "@server/fish/fishers"; +import { reel } from "./reel"; export const go = new Command( "go", @@ -33,7 +35,12 @@ export const go = new Command( return `The place "${args[0]}" is not ${prefix}${nearby.aliases[0]}.`; inventory.location = nextLoc.id; - updateInventory(inventory); + await updateInventory(inventory); + + if (getFishing(id, user.id)) { + stopFishing(id, user.id, false); + return `You ${prefix}${reel.aliases[0]}ed your LURE in and went to ${nextLoc.name}.`; + } return `You went to ${nextLoc.name}.`; } diff --git a/src/api/commands/groups/fishing/nearby.ts b/src/api/commands/groups/fishing/nearby.ts index 6b808f1..2f2ffe4 100644 --- a/src/api/commands/groups/fishing/nearby.ts +++ b/src/api/commands/groups/fishing/nearby.ts @@ -5,7 +5,7 @@ import { locations } from "@server/fish/locations"; export const nearby = new Command( "nearby", - ["nearby"], + ["nearby", "noorby", "n"], "Look at nearby locations", "nearby", "command.fishing.nearby", diff --git a/src/api/commands/groups/fishing/reel.ts b/src/api/commands/groups/fishing/reel.ts index 8eeb2d9..63f7743 100644 --- a/src/api/commands/groups/fishing/reel.ts +++ b/src/api/commands/groups/fishing/reel.ts @@ -5,7 +5,7 @@ import { locations } from "@server/fish/locations"; export const reel = new Command( "reel", - ["reel"], + ["reel", "rool", "stopfishing", "stopfoshing"], "Reel in and stop fishing", "fishing", "command.fishing.reel", @@ -15,7 +15,7 @@ export const reel = new Command( stopFishing(id, part.id); return `Our friend ${part.name} reel his/her lure back inside, temporarily decreasing his/her chances of catching a fish by 100%.`; } else { - return `Friend ${part.name}: You haven't /casted it.`; + return `Friend ${part.name}: You haven't ${prefix}casted it.`; } } ); diff --git a/src/api/commands/groups/general/color.ts b/src/api/commands/groups/general/color.ts new file mode 100644 index 0000000..24e659a --- /dev/null +++ b/src/api/commands/groups/general/color.ts @@ -0,0 +1,24 @@ +import Command from "@server/commands/Command"; +import { commandGroups } from ".."; +import { logger } from "@server/commands/handler"; +import { CosmicColor } from "@util/CosmicColor"; + +export const color = new Command( + "color", + ["color"], + "Get the name of a color", + "color", + "command.general.color", + async ({ id, command, args, prefix, part, user }) => { + let color = args[0]; + let out1 = `Friend ${part.name}: That color is`; + + if (!color) { + color = part.color; + out1 = `Friend ${part.name}, your color is`; + } + + const c = new CosmicColor(color); + return `${out1} ${c.getName().toLowerCase()}.`; + } +); diff --git a/src/api/commands/groups/index.ts b/src/api/commands/groups/index.ts index 33f1214..5aac5b6 100644 --- a/src/api/commands/groups/index.ts +++ b/src/api/commands/groups/index.ts @@ -13,6 +13,10 @@ import { eat } from "./inventory/eat"; import { sack } from "./inventory/sack"; import { reel } from "./fishing/reel"; import { memory } from "./util/mem"; +import { pokemon } from "./inventory/pokemon"; +import { color } from "./general/color"; +import { autofish } from "./util/autofish"; +import { pokedex } from "./util/pokedex"; interface ICommandGroup { id: string; @@ -25,7 +29,7 @@ export const commandGroups: ICommandGroup[] = []; const generalGroup: ICommandGroup = { id: "general", displayName: "General", - commands: [help] + commands: [help, color] }; commandGroups.push(generalGroup); @@ -41,7 +45,7 @@ commandGroups.push(fishingGroup); const inventoryGroup: ICommandGroup = { id: "inventory", displayName: "Inventory", - commands: [inventory, take, eat, sack] + commands: [inventory, take, eat, sack, pokemon] }; commandGroups.push(inventoryGroup); @@ -49,7 +53,7 @@ commandGroups.push(inventoryGroup); const utilGroup: ICommandGroup = { id: "util", displayName: "Utility", - commands: [data, setcolor, memory] + commands: [data, setcolor, memory, autofish, pokedex] }; commandGroups.push(utilGroup); diff --git a/src/api/commands/groups/inventory/eat.ts b/src/api/commands/groups/inventory/eat.ts index 6637bfd..1cb2b14 100644 --- a/src/api/commands/groups/inventory/eat.ts +++ b/src/api/commands/groups/inventory/eat.ts @@ -7,7 +7,7 @@ import { CosmicColor } from "@util/CosmicColor"; export const eat = new Command( "eat", ["eat", "oot"], - "Eat literally anything in your inventory", + "Eat literally anything you have (except non-fish animals)", "eat ", "command.inventory.eat", async ({ id, command, args, prefix, part, user }) => { @@ -48,30 +48,31 @@ export const eat = new Command( i = 0; - for (const pokemon of inventory.pokemon as TPokemonSack) { - if (!pokemon.name.toLowerCase().includes(eating.toLowerCase())) { - i++; - continue; - } + // no more eating animals + // for (const pokemon of inventory.pokemon as TPokemonSack) { + // if (!pokemon.name.toLowerCase().includes(eating.toLowerCase())) { + // i++; + // continue; + // } - foundObject = pokemon as unknown as IObject; + // foundObject = pokemon as unknown as IObject; - let shouldRemove = false; + // let shouldRemove = false; - if (typeof pokemon.count !== "undefined") { - if (pokemon.count > 1) { - shouldRemove = false; - ((inventory.pokemon as TPokemonSack)[i].count as number)--; - } else { - shouldRemove = true; - } - } else { - shouldRemove = true; - } + // if (typeof pokemon.count !== "undefined") { + // if (pokemon.count > 1) { + // shouldRemove = false; + // ((inventory.pokemon as TPokemonSack)[i].count as number)--; + // } else { + // shouldRemove = true; + // } + // } else { + // shouldRemove = true; + // } - if (shouldRemove) (inventory.pokemon as TPokemonSack).splice(i, 1); - break; - } + // if (shouldRemove) (inventory.pokemon as TPokemonSack).splice(i, 1); + // break; + // } i = 0; diff --git a/src/api/commands/groups/inventory/inventory.ts b/src/api/commands/groups/inventory/inventory.ts index 0317751..25f142e 100644 --- a/src/api/commands/groups/inventory/inventory.ts +++ b/src/api/commands/groups/inventory/inventory.ts @@ -1,5 +1,7 @@ +import type { User } from "@prisma/client"; import Command from "@server/commands/Command"; import { getInventory } from "@server/data/inventory"; +import prisma from "@server/data/prisma"; export const inventory = new Command( "inventory", @@ -8,20 +10,59 @@ export const inventory = new Command( "inventory", "command.inventory.inventory", async ({ id, command, args, prefix, part, user }) => { - const inv = await getInventory(user.inventoryId); - if (!inv) return; + if (args[0]) { + let decidedUser: User = user; + decidedUser = (await prisma.user.findFirst({ + where: { + name: { + contains: args[0] + } + } + })) as User; - const items = inv.items as unknown as IItem[]; + if (!decidedUser) + decidedUser = (await prisma.user.findFirst({ + where: { + id: { + contains: args[0] + } + } + })) as User; - return `Inventory: ${ - items - .map( - item => - `${item.emoji || ""}${item.name}${ - item.count ? ` (x${item.count})` : "" - }` - ) - .join(", ") || "(none)" - }`; + if (!decidedUser) return `User "${args[0]}" not found.`; + + const inv = await getInventory(decidedUser.inventoryId); + if (!inv) + return `This message should be impossible to see because friend ${decidedUser.name}'s items inventory (and, by extension, their entire inventory) does not exist.`; + + const items = inv.items as TInventoryItems; + + return `Contents of ${decidedUser.name}'s inventory: ${ + items + .map( + (item: IItem) => + `${item.emoji || "📦"}${item.name}${ + item.count ? ` (x${item.count})` : "" + }` + ) + .join(", ") || "(none)" + }`; + } else { + const inv = await getInventory(user.inventoryId); + if (!inv) + return `Apparently, you have no inventory. Not sure if that can be fixed, and I don't know how you got this message.`; + const items = inv.items as TInventoryItems; + + return `Contents of ${part.name}'s inventory: ${ + items + .map( + (item: IItem) => + `${item.emoji || "📦"}${item.name}${ + item.count ? ` (x${item.count})` : "" + }` + ) + .join(", ") || "(none)" + }`; + } } ); diff --git a/src/api/commands/groups/inventory/pokemon.ts b/src/api/commands/groups/inventory/pokemon.ts new file mode 100644 index 0000000..95e2c68 --- /dev/null +++ b/src/api/commands/groups/inventory/pokemon.ts @@ -0,0 +1,28 @@ +import Command from "@server/commands/Command"; +import { getInventory } from "@server/data/inventory"; + +export const pokemon = new Command( + "pokemon", + ["pokemon"], + "Look at your Pokemon", + "pokemon", + "command.inventory.pokemon", + async ({ id, command, args, prefix, part, user }) => { + const inv = await getInventory(user.inventoryId); + if (!inv) return; + + const sack = inv.pokemon as TPokemonSack[]; + + return `Friend ${part.name}'s Pokémon: ${ + sack + .map( + (pokemon: IPokemon) => + `${pokemon.emoji || ""}${pokemon.name}${ + pokemon.count ? ` (x${pokemon.count})` : "" + }` + ) + .join(", ") || "(none)" + }`; + }, + true +); diff --git a/src/api/commands/groups/inventory/take.ts b/src/api/commands/groups/inventory/take.ts index 4830947..916e4be 100644 --- a/src/api/commands/groups/inventory/take.ts +++ b/src/api/commands/groups/inventory/take.ts @@ -49,7 +49,8 @@ export const take = new Command( addItem(fish, foundObject); break; case "pokemon": - addItem(pokemon as unknown as IObject[], foundObject); + // addItem(pokemon as unknown as IObject[], foundObject); + return "Unlike other items, Pokémon have to be caught."; break; default: break; diff --git a/src/api/commands/groups/util/autofish.ts b/src/api/commands/groups/util/autofish.ts new file mode 100644 index 0000000..eeb84db --- /dev/null +++ b/src/api/commands/groups/util/autofish.ts @@ -0,0 +1,30 @@ +import Command from "@server/commands/Command"; +import { prefixes } from "@server/commands/prefixes"; +import { getFishing, startFishing } from "@server/fish/fishers"; +import { reel } from "../fishing/reel"; + +export const autofish = new Command( + "autofish", + ["autofish"], + "Fish automatically", + "data", + "command.util.autofish", + async props => { + const fishing = getFishing(props.id, props.part.id); + + if (!fishing) { + startFishing(props.id, props.part.id, true, true); + return `Our friend ${props.user.name} casts LURE into a water with AUTOFISH enabled. (${props.prefix}${reel.aliases[0]} to disable)`; + } else { + return `Your lure is already in the water (since ${( + (Date.now() - fishing.t) / + 1000 / + 60 + ).toFixed(2)} minutes ago).${ + fishing.autofish + ? ` (AUTOFISH is enabled)` + : ` (${props.prefix}${reel.aliases[0]} in first to AUTOFISH)` + }`; + } + } +); diff --git a/src/api/commands/groups/util/pokedex.ts b/src/api/commands/groups/util/pokedex.ts new file mode 100644 index 0000000..faa8dfc --- /dev/null +++ b/src/api/commands/groups/util/pokedex.ts @@ -0,0 +1,30 @@ +import Command from "@server/commands/Command"; +import { getPokemonByID } from "@server/pokemon/pokedex"; + +export const pokedex = new Command( + "pokedex", + ["pokedex", "dex"], + "View a Pokémon in the Pokédex", + "pokedex", + "command.util.pokedex", + async ({ args }) => { + const num = parseInt(args[0]); + if (isNaN(num)) return `Please provide a Pokémon ID.`; + + const pokemon = getPokemonByID(num); + + if (!pokemon) return `Pokémon with ID ${args[0]} not found.`; + + return `ID: ${pokemon.pokeID} // Name: ${ + pokemon.name + } // Type: ${pokemon.type.join("/")} // Base HP: ${ + pokemon.base.HP + } // Base Attack: ${pokemon.base.Attack} // Base Defense: ${ + pokemon.base.Defense + } // Base Sp. Attack: ${ + pokemon.base["Sp. Attack"] + } // Base Sp. Defense: ${pokemon.base["Sp. Defense"]} // Base Speed: ${ + pokemon.base.Speed + }`; + } +); diff --git a/src/api/commands/handler.ts b/src/api/commands/handler.ts index a9bfb23..8dc7c3c 100644 --- a/src/api/commands/handler.ts +++ b/src/api/commands/handler.ts @@ -11,7 +11,8 @@ export async function handleCommand( command: string, args: string[], prefix: string, - part: IPart + part: IPart, + isDM: boolean = false ): Promise { let foundCommand: Command | undefined; @@ -60,7 +61,8 @@ export async function handleCommand( args, prefix, part, - user + user, + isDM: isDM ?? false }); if (response) return { response }; diff --git a/src/api/fish/fishers.ts b/src/api/fish/fishers.ts index fb8358b..b61b0b6 100644 --- a/src/api/fish/fishers.ts +++ b/src/api/fish/fishers.ts @@ -9,14 +9,7 @@ import { addBack } from "@server/backs"; import { prefixes } from "@server/commands/prefixes"; import { Logger } from "@util/Logger"; -export let fishers: Record< - string, - { - id: string; - userId: string; - t: number; - } -> = {}; +export let fishers: Record = {}; let cooldown = Date.now() + 5000; @@ -33,24 +26,24 @@ export async function tick() { if (!winner) return; - const user = await getUser(winner.userId); + const user = await getUser(winner.userID); if (!user) { - stopFishing(winner.id, winner.userId); + stopFishing(winner.id, winner.userID, false); return; } const inventory = await getInventory(user.inventoryId); if (!inventory) { - stopFishing(winner.id, winner.userId); + stopFishing(winner.id, winner.userID, false); return; } const r = Math.random(); if (r < 0.1) { - stopFishing(winner.id, winner.userId); + stopFishing(winner.id, winner.userID, winner.autofish); const animal = randomFish(inventory.location); addItem(inventory.fishSack as TFishSack, animal); await updateInventory(inventory); @@ -63,14 +56,18 @@ export async function tick() { ? "large" : animal.size < 100 ? "huge" - : "massive"; + : animal.size < 200 + ? "massive" + : "gigantic"; addBack(winner.id, { m: "sendchat", message: `Our good friend @${user.id} caught a ${size} ${ animal.emoji || "🐟" }${animal.name}! ready to ${prefixes[0]}eat or ${ prefixes[0] - }fish again` + }fish again${winner.autofish ? " (AUTOFISH is enabled)" : ""}`, + isDM: winner.isDM, + id: winner.userID }); } } @@ -79,14 +76,7 @@ export async function tick() { } export async function startFisherTick() { - let maybe = (await kvGet("fishers")) as Record< - string, - { - id: string; - userId: string; - t: number; - } - >; + let maybe = (await kvGet("fishers")) as Record; if (maybe) fishers = maybe; @@ -97,13 +87,33 @@ export function stopFisherTick() { removeTickEvent(tick); } -export function startFishing(id: string, userId: string) { - fishers[id + "~" + userId] = { id, userId: userId, t: Date.now() }; +export function startFishing( + id: string, + userId: string, + isDM: boolean = false, + autofish: boolean = false +) { + fishers[id + "~" + userId] = { + id, + userID: userId, + t: Date.now(), + isDM, + autofish + }; } -export function stopFishing(id: string, userId: string) { +export function stopFishing( + id: string, + userId: string, + autofish: boolean = false +) { let key = id + "~" + userId; + let fisher = fishers[key]; delete fishers[key]; + + if (autofish) { + startFishing(id, userId, true, true); + } } export function getFishing(id: string, userId: string) { diff --git a/src/api/pokemon/pokedex.ts b/src/api/pokemon/pokedex.ts new file mode 100644 index 0000000..b3599cf --- /dev/null +++ b/src/api/pokemon/pokedex.ts @@ -0,0 +1,10 @@ +import { Logger } from "@util/Logger"; +import { loadConfig } from "@util/config"; + +export const pokedex = loadConfig("config/pokedex.yml", []); + +const logger = new Logger("Pokédex"); + +export function getPokemonByID(id: number) { + return pokedex.find(p => p.pokeID == id); +} diff --git a/src/mpp/bot/Bot.ts b/src/mpp/bot/Bot.ts index d43d9e1..3d93735 100644 --- a/src/mpp/bot/Bot.ts +++ b/src/mpp/bot/Bot.ts @@ -141,7 +141,8 @@ export class MPPNetBot { id: msg.sender._id, name: msg.sender.name, color: msg.sender.color - } + }, + isDM: true }); if (!command) return; @@ -178,7 +179,12 @@ export class MPPNetBot { }); this.b.on("sendchat", msg => { - this.sendChat(msg.message); + // this.logger.debug("sendchat message:", msg); + if (msg.isDM) { + this.sendDM(msg.message, msg.id); + } else { + this.sendChat(msg.message); + } }); } @@ -218,7 +224,7 @@ export class MPPNetBot { .split("\r") .join("")}`, _id: dm, - reply_to: reply_to + reply_to } ]); } else { diff --git a/src/util/types.d.ts b/src/util/types.d.ts index 44583d6..6ddd3ec 100644 --- a/src/util/types.d.ts +++ b/src/util/types.d.ts @@ -15,6 +15,7 @@ type TCommandCallback = (props: { prefix: string; part: IPart; user: User; + isDM: boolean; }) => Promise; interface CountComponent { @@ -51,6 +52,7 @@ interface IFish extends IObject { interface IPokemon extends IObject { id: number; + pokeID: number; objtype: "pokemon"; emoji?: string; name: string; @@ -98,3 +100,13 @@ interface ILocation { objects: IObject[]; hasSand: boolean; } + +interface TFisher { + id: string; + userID: string; + t: number; + isDM: boolean; + autofish: boolean; +} + +type TPokedex = IPokemon[];