Make fish work

This commit is contained in:
Hri7566 2024-02-22 20:27:51 -05:00
parent fa844d37cb
commit ccdc021e96
18 changed files with 1539 additions and 52 deletions

View File

@ -40,3 +40,8 @@ model LocationObjectStorage {
id String @id id String @id
objects Json @default("[]") objects Json @default("[]")
} }
model KeyValueStore {
id Int @id @default(0)
json Json @default("{}")
}

View File

@ -1,4 +1,5 @@
import Command from "@server/commands/Command"; import Command from "@server/commands/Command";
import { getFishing, startFishing } from "@server/fish/fishers";
export const fish = new Command( export const fish = new Command(
"fish", "fish",
@ -7,6 +8,17 @@ export const fish = new Command(
"fish", "fish",
"command.fishing.fish", "command.fishing.fish",
async ({ id, command, args, prefix, part, user }) => { async ({ id, command, args, prefix, part, user }) => {
return "There is no fishing yet, please come back later when I write the code for it"; const fishing = getFishing(id, part.id);
if (!fishing) {
startFishing(id, part.id);
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).`;
}
} }
); );

View File

@ -0,0 +1,21 @@
import Command from "@server/commands/Command";
import { getInventory } from "@server/data/inventory";
import { getFishing, stopFishing } from "@server/fish/fishers";
import { locations } from "@server/fish/locations";
export const reel = new Command(
"reel",
["reel"],
"Reel in and stop fishing",
"fishing",
"command.fishing.reel",
async ({ id, command, args, prefix, part, user }) => {
const fishing = getFishing(id, part.id);
if (fishing) {
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.`;
}
}
);

View File

@ -23,16 +23,20 @@ export const help = new Command(
"command.general.help", "command.general.help",
async ({ id, command, args, prefix, part, user }) => { async ({ id, command, args, prefix, part, user }) => {
if (!args[0]) { if (!args[0]) {
return `__Fishing:__\n${commandGroups const list = [];
.map(
group => for (const group of commandGroups) {
`${group.displayName}: ${group.commands let list2 = [];
.map(cmd =>
cmd.visible ? cmd.aliases[0] : "<hidden>" for (const cmd of group.commands) {
) if (cmd.visible) list2.push(cmd.aliases[0]);
.join(", ")}` }
)
.join("\n")}`; if (list2.length > 0)
list.push(`${group.displayName}: ${list2.join(", ")}`);
}
return `__Fishing:__\n${list.join("\n")}`;
} else { } else {
const commands = commandGroups.flatMap(group => group.commands); const commands = commandGroups.flatMap(group => group.commands);

View File

@ -1,25 +0,0 @@
import Command from "@server/commands/Command";
import { getInventory } from "@server/data/inventory";
export const inventory = new Command(
"inventory",
["inventory", "inv"],
"Look at your inventory",
"data",
"command.util.inventory",
async ({ id, command, args, prefix, part, user }) => {
const inventory = await getInventory(user.inventoryId);
if (!inventory) return;
const items = inventory.items as unknown as IItem[];
return `Inventory: ${items
.map(
item =>
`${item.emoji || ""}${item.name}${
item.count ? ` (x${item.count})` : ""
}`
)
.join(", ")}`;
}
);

View File

@ -7,8 +7,11 @@ import { location } from "./fishing/location";
import { go } from "./fishing/go"; import { go } from "./fishing/go";
import { nearby } from "./fishing/nearby"; import { nearby } from "./fishing/nearby";
import { look } from "./fishing/look"; import { look } from "./fishing/look";
import { take } from "./fishing/take"; import { take } from "./inventory/take";
import { inventory } from "./general/inventory"; import { inventory } from "./inventory/inventory";
import { eat } from "./inventory/eat";
import { sack } from "./inventory/sack";
import { reel } from "./fishing/reel";
interface ICommandGroup { interface ICommandGroup {
id: string; id: string;
@ -18,26 +21,34 @@ interface ICommandGroup {
export const commandGroups: ICommandGroup[] = []; export const commandGroups: ICommandGroup[] = [];
const general: ICommandGroup = { const generalGroup: ICommandGroup = {
id: "general", id: "general",
displayName: "General", displayName: "General",
commands: [help, inventory] commands: [help]
}; };
commandGroups.push(general); commandGroups.push(generalGroup);
const fishing: ICommandGroup = { const fishingGroup: ICommandGroup = {
id: "fishing", id: "fishing",
displayName: "Fishing", displayName: "Fishing",
commands: [fish, location, go, nearby, look, take] commands: [fish, reel, location, go, nearby, look]
}; };
commandGroups.push(fishing); commandGroups.push(fishingGroup);
const util: ICommandGroup = { const inventoryGroup: ICommandGroup = {
id: "inventory",
displayName: "Inventory",
commands: [inventory, take, eat, sack]
};
commandGroups.push(inventoryGroup);
const utilGroup: ICommandGroup = {
id: "util", id: "util",
displayName: "Utility", displayName: "Utility",
commands: [data, setcolor] commands: [data, setcolor]
}; };
commandGroups.push(util); commandGroups.push(utilGroup);

View File

@ -0,0 +1,148 @@
import { addBack } from "@server/backs";
import Command from "@server/commands/Command";
import { logger } from "@server/commands/handler";
import { getInventory, updateInventory } from "@server/data/inventory";
import { CosmicColor } from "@util/CosmicColor";
export const eat = new Command(
"eat",
["eat"],
"Eat literally anything in your inventory",
"eat <something>",
"command.inventory.eat",
async ({ id, command, args, prefix, part, user }) => {
const eating = args[0];
if (!eating) return `What do you want to ${prefix}eat?`;
const inventory = await getInventory(user.inventoryId);
if (!inventory) return;
let foundObject: IObject | undefined;
let tryChangingColor = false;
for (const item of inventory.items as unknown as IItem[]) {
if (!item.name.toLowerCase().includes(eating.toLowerCase()))
continue;
foundObject = item;
let shouldRemove = false;
if (item.count) {
if (item.count > 1) {
shouldRemove = false;
((inventory.items as unknown as IItem[])[
(inventory.items as unknown as IItem[]).indexOf(item)
].count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove)
(inventory.items as unknown as IItem[]).splice(
(inventory.items as unknown as IItem[]).indexOf(item, 1)
);
break;
}
for (const pokemon of inventory.pokemon as unknown as IPokemon[]) {
if (!pokemon.name.toLowerCase().includes(eating.toLowerCase()))
continue;
foundObject = pokemon as unknown as IObject;
let shouldRemove = false;
if (pokemon.count) {
if (pokemon.count > 1) {
shouldRemove = false;
((inventory.pokemon as unknown as IPokemon[])[
(inventory.pokemon as unknown as IPokemon[]).indexOf(
pokemon
)
].count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove)
(inventory.pokemon as unknown as IPokemon[]).splice(
(inventory.pokemon as unknown as IPokemon[]).indexOf(
pokemon,
1
)
);
break;
}
for (const fish of inventory.fishSack as unknown as IFish[]) {
if (!fish.name.toLowerCase().includes(eating.toLowerCase()))
continue;
foundObject = fish as unknown as IObject;
let shouldRemove = false;
if (fish.count) {
if (fish.count > 1) {
shouldRemove = false;
((inventory.fishSack as unknown as IFish[])[
(inventory.fishSack as unknown as IFish[]).indexOf(fish)
].count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove)
(inventory.fishSack as unknown as IFish[]).splice(
(inventory.fishSack as unknown as IFish[]).indexOf(fish, 1)
);
break;
}
if (!foundObject) return `You don't have "${eating}" to eat.`;
if (foundObject.objtype == "fish") {
tryChangingColor = true;
}
await updateInventory(inventory);
if (!tryChangingColor) {
if (foundObject.id == "sand") {
return `Our friend ${part.name} ate of his/her ${foundObject.name}.`;
} else {
return `Our friend ${part.name} ate his/her ${foundObject.name}.`;
}
} else {
const r = Math.random();
if (r < 0.3) {
const color = new CosmicColor(
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255)
);
addBack(id, {
m: "color",
id: part.id,
color: color.toHexa()
});
return `Our friend ${part.name} at his/her ${
foundObject.name
} and it made him/her turn ${color.getName().toLowerCase()}.`;
} else {
return `Our friend ${part.name} at his/her ${foundObject.name}.`;
}
}
}
);

View File

@ -0,0 +1,27 @@
import Command from "@server/commands/Command";
import { getInventory } from "@server/data/inventory";
export const inventory = new Command(
"inventory",
["inventory", "inv", "i"],
"Look at your inventory",
"inventory",
"command.inventory.inventory",
async ({ id, command, args, prefix, part, user }) => {
const inv = await getInventory(user.inventoryId);
if (!inv) return;
const items = inv.items as unknown as IItem[];
return `Inventory: ${
items
.map(
item =>
`${item.emoji || ""}${item.name}${
item.count ? ` (x${item.count})` : ""
}`
)
.join(", ") || "(none)"
}`;
}
);

View File

@ -0,0 +1,27 @@
import Command from "@server/commands/Command";
import { getInventory } from "@server/data/inventory";
export const sack = new Command(
"sack",
["sack", "caught"],
"Look at your fish sack",
"sack",
"command.inventory.sack",
async ({ id, command, args, prefix, part, user }) => {
const inv = await getInventory(user.inventoryId);
if (!inv) return;
const fishSack = inv.fishSack as TFishSack;
return `Contents of ${part.name}'s fish sack: ${
fishSack
.map(
(fish: IFish) =>
`${fish.emoji || ""}${fish.name}${
fish.count ? ` (x${fish.count})` : ""
}`
)
.join(", ") || "(none)"
}`;
}
);

View File

@ -1,8 +1,8 @@
import Command from "@server/commands/Command"; import Command from "@server/commands/Command";
import { logger } from "@server/commands/handler"; import { logger } from "@server/commands/handler";
import { getInventory, updateInventory } from "@server/data/inventory"; import { getInventory, updateInventory } from "@server/data/inventory";
import { locations } from "@server/fish/locations"; import { locations, saveObjects } from "@server/fish/locations";
import { go } from "./go"; import { go } from "../fishing/go";
import { addItem } from "@server/items"; import { addItem } from "@server/items";
export const take = new Command( export const take = new Command(
@ -10,7 +10,7 @@ export const take = new Command(
["take"], ["take"],
"Take something from your surroundings", "Take something from your surroundings",
"take <something>", "take <something>",
"command.fishing.take", "command.inventory.take",
async ({ id, command, args, prefix, part, user }) => { async ({ id, command, args, prefix, part, user }) => {
let taking = args[0]; let taking = args[0];
@ -34,6 +34,9 @@ export const take = new Command(
for (const obj of loc.objects) { for (const obj of loc.objects) {
if (obj.name.toLowerCase().includes(taking.toLowerCase())) if (obj.name.toLowerCase().includes(taking.toLowerCase()))
foundObject = obj; foundObject = obj;
loc.objects.splice(loc.objects.indexOf(obj), 1);
await saveObjects();
} }
if (!foundObject) return `There is no "${taking}" here.`; if (!foundObject) return `There is no "${taking}" here.`;
@ -57,6 +60,7 @@ export const take = new Command(
(inventory as any).pokemon = pokemon; (inventory as any).pokemon = pokemon;
await updateInventory(inventory); await updateInventory(inventory);
return `You picked up the ${foundObject.name}.`; return `You picked up the ${foundObject.name}.`;
} }
); );

View File

@ -8,5 +8,6 @@ export const data = new Command(
"command.util.data", "command.util.data",
async props => { async props => {
return JSON.stringify(props); return JSON.stringify(props);
} },
false
); );

View File

@ -17,5 +17,6 @@ export const setcolor = new Command(
}); });
return "Attempting to set color."; return "Attempting to set color.";
} },
false
); );

View File

@ -0,0 +1,37 @@
import prisma from "./prisma";
export async function createKeyValueStore() {
return await prisma.keyValueStore.create({
data: {}
});
}
export async function getkvInternal() {
const kv = await prisma.keyValueStore.findUnique({
where: { id: 0 }
});
if (!kv) return await createKeyValueStore();
return kv;
}
export async function kvSet(key: string, value: any) {
const kv = await getkvInternal();
(kv.json as any)[key] = value;
return await prisma.keyValueStore.update({
where: {
id: 0
},
data: {
json: kv.json as any
}
});
}
export async function kvGet(key: string) {
const kv = await getkvInternal();
return (kv.json as any)[key];
}

111
src/api/fish/fishers.ts Normal file
View File

@ -0,0 +1,111 @@
import { kvGet, kvSet } from "@server/data/keyValueStore";
import { getObjectStorage } from "@server/data/location";
import { addTickEvent, removeTickEvent } from "@util/tick";
import { randomFish } from "./fish";
import { getUser } from "@server/data/user";
import { getInventory, updateInventory } from "@server/data/inventory";
import { addItem } from "@server/items";
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;
}
> = {};
let cooldown = Date.now() + 5000;
const logger = new Logger("Fishermen");
export async function tick() {
if (Date.now() > cooldown) {
cooldown = Date.now() + 5000;
let winner =
Object.values(fishers)[
Math.floor(Math.random() * Object.values(fishers).length)
];
if (!winner) return;
const user = await getUser(winner.userId);
if (!user) {
stopFishing(winner.id, winner.userId);
return;
}
const inventory = await getInventory(user.inventoryId);
if (!inventory) {
stopFishing(winner.id, winner.userId);
return;
}
const r = Math.random();
if (r < 0.1) {
stopFishing(winner.id, winner.userId);
const animal = randomFish(inventory.location);
addItem(inventory.fishSack as TFishSack, animal);
await updateInventory(inventory);
const size =
animal.size < 30
? "small"
: animal.size < 60
? "medium-sized"
: animal.size < 75
? "large"
: animal.size < 100
? "huge"
: "massive";
addBack(winner.id, {
m: "sendchat",
message: `Our good friend ${user.name} caught a ${size} ${
animal.emoji || "🐟"
}${animal.name}! ready to ${prefixes[0]}eat or ${
prefixes[0]
}fish again`
});
}
}
await kvSet("fishers", fishers);
}
export async function startFisherTick() {
let maybe = (await kvGet("fishers")) as Record<
string,
{
id: string;
userId: string;
t: number;
}
>;
if (maybe) fishers = maybe;
addTickEvent(tick);
}
export function stopFisherTick() {
removeTickEvent(tick);
}
export function startFishing(id: string, userId: string) {
fishers[id + "~" + userId] = { id, userId: userId, t: Date.now() };
}
export function stopFishing(id: string, userId: string) {
let key = id + "~" + userId;
delete fishers[key];
}
export function getFishing(id: string, userId: string) {
return fishers[id + "~" + userId];
}

View File

@ -1,5 +1,7 @@
import "./api/server"; import "./api/server";
import "./cli/readline"; import "./cli/readline";
import { startFisherTick } from "./fish/fishers";
import { startObjectTimers } from "./fish/locations"; import { startObjectTimers } from "./fish/locations";
startObjectTimers(); startObjectTimers();
await startFisherTick();

View File

@ -153,7 +153,7 @@ export class MPPNetBot {
try { try {
const backs = (await trpc.backs.query()) as IBack<unknown>[]; const backs = (await trpc.backs.query()) as IBack<unknown>[];
if (backs.length > 0) { if (backs.length > 0) {
this.logger.debug(backs); // this.logger.debug(backs);
for (const back of backs) { for (const back of backs) {
if (typeof back.m !== "string") return; if (typeof back.m !== "string") return;
this.b.emit(back.m, back); this.b.emit(back.m, back);
@ -175,6 +175,10 @@ export class MPPNetBot {
} }
]); ]);
}); });
this.b.on("sendchat", msg => {
this.sendChat(msg.message);
});
} }
public sendChat(text: string, reply?: string) { public sendChat(text: string, reply?: string) {

1085
src/util/CosmicColor.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
import { kvGet, kvSet } from "@server/data/keyValueStore";
import { test, expect } from "bun:test";
test("Key value store saves, loads, and deletes", async () => {
await kvSet("test", 1);
const val = await kvGet("test");
expect(val).toBe(1);
await kvSet("test", undefined);
const val2 = await kvGet("test");
expect(val2).toBeUndefined();
});