Finish item behaviors, implement kekklefruit, remove forest

This commit is contained in:
Hri7566 2024-03-05 16:16:49 -05:00
parent af89379aa2
commit 5565668088
9 changed files with 213 additions and 114 deletions

View File

@ -1,7 +1,6 @@
import { addBack } from "@server/backs";
import Command from "@server/commands/Command";
import { getInventory, updateInventory } from "@server/data/inventory";
import { CosmicColor } from "@util/CosmicColor";
import { itemBehaviorMap, runBehavior } from "@server/items/behavior";
export const eat = new Command(
"eat",
@ -9,7 +8,8 @@ export const eat = new Command(
"Eat literally anything you have (except non-fish animals)",
"eat <something>",
"command.inventory.eat",
async ({ id, command, args, prefix, part, user }) => {
async props => {
const { args, prefix, part, user } = props;
const eating = args[0];
if (!eating) return `What do you want to ${prefix}eat?`;
@ -17,8 +17,8 @@ export const eat = new Command(
if (!inventory) return;
let foundObject: IObject | undefined;
let tryChangingColor = false;
let i = 0;
let shouldRemove = false;
for (const item of inventory.items as unknown as IItem[]) {
if (!item.name.toLowerCase().includes(eating.toLowerCase())) {
@ -27,54 +27,11 @@ export const eat = new Command(
}
foundObject = item;
let shouldRemove = false;
if (typeof item.count !== "undefined") {
if (item.count > 1) {
shouldRemove = false;
((inventory.items as TInventoryItems)[i].count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove) (inventory.items as TInventoryItems).splice(i, 1);
break;
}
i = 0;
// 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;
// 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 (shouldRemove) (inventory.pokemon as TPokemonSack).splice(i, 1);
// break;
// }
i = 0;
for (const fish of inventory.fishSack as TFishSack) {
if (!fish.name.toLowerCase().includes(eating.toLowerCase())) {
i++;
@ -82,56 +39,77 @@ export const eat = new Command(
}
foundObject = fish;
let shouldRemove = false;
if (typeof fish.count !== "undefined") {
if (fish.count > 1) {
shouldRemove = false;
((inventory.fishSack as TFishSack)[i].count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove) (inventory.fishSack as TFishSack).splice(i, 1);
break;
}
if (!foundObject) return `You don't have "${eating}" to eat.`;
if (foundObject.objtype == "fish") {
tryChangingColor = true;
// Get item behaviors and run the "eat" script
let thingy = foundObject.id;
if (foundObject.objtype == "fish") thingy = "fish";
const bhv = itemBehaviorMap[thingy];
if (!bhv) return `The ${foundObject.name} isn't edible.`;
if (!bhv["eat"]) return `You can't eat the ${foundObject.name}.`;
const res = await runBehavior(thingy, "eat", foundObject, props);
shouldRemove = res.shouldRemove;
if (shouldRemove) {
if (foundObject.objtype == "fish") {
i = 0;
for (const fish of inventory.fishSack as TFishSack) {
if (typeof fish.count !== "undefined") {
if (fish.count > 1) {
shouldRemove = false;
((inventory.fishSack as TFishSack)[i]
.count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove)
(inventory.fishSack as TFishSack).splice(i, 1);
break;
}
} else if (foundObject.objtype == "item") {
i = 0;
for (const item of inventory.items as unknown as IItem[]) {
if (typeof item.count == "number") {
if (item.count > 1) {
shouldRemove = false;
((inventory.items as TInventoryItems)[i]
.count as number)--;
} else {
shouldRemove = true;
}
} else {
shouldRemove = true;
}
if (shouldRemove)
(inventory.items as TInventoryItems).splice(i, 1);
break;
}
}
await updateInventory(inventory);
}
await updateInventory(inventory);
if (!tryChangingColor) {
if (foundObject.id == "sand") {
return `Our friend ${part.name} ate of his/her ${foundObject.name}.`;
if (foundObject.id == "sand") {
if (res.and) {
return `Our friend ${part.name} ate of his/her ${foundObject.name} ${res.and}`;
} else {
return `Our friend ${part.name} ate his/her ${foundObject.name}.`;
return `Our friend ${part.name} ate of 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} ate his/her ${
foundObject.name
} and it made him/her turn ${color.getName().toLowerCase()}.`;
if (res.and) {
return `Our friend ${part.name} ate his/her ${foundObject.name} ${res.and}`;
} else {
return `Our friend ${part.name} ate his/her ${foundObject.name}.`;
}

View File

@ -41,8 +41,11 @@ export async function tick() {
}
const r = Math.random();
const data = await getFishingChance(user.id);
// After 30 minutes, reset chance
if (data.t > 30 * 60000) await resetFishingChance(user.id);
if (r < 0.1) {
if (r < data.chance / 10) {
stopFishing(
winner.id,
winner.userID,
@ -131,3 +134,33 @@ export function stopFishing(
export function getFishing(id: string, userID: string) {
return fishers[id + "~" + userID];
}
export async function getFishingChance(userID: string) {
const key = `fishingChance~${userID}`;
const data = (await kvGet(key)) as IFishingChance;
if (!data) {
await resetFishingChance(userID);
return await getFishingChance(userID);
}
return data;
}
export async function resetFishingChance(userID: string) {
const key = `fishingChance~${userID}`;
await kvSet(key, {
t: Date.now(),
chance: 1
} as IFishingChance);
}
export async function incrementFishingChance(userID: string) {
const key = `fishingChance~${userID}`;
const data = (await kvGet(key)) as IFishingChance;
if (!data) await resetFishingChance(userID);
const r = Math.random();
data.chance += r;
await kvSet(key, data);
return r;
}

View File

@ -35,14 +35,6 @@ export const locations = loadConfig<ILocation[]>("config/locations.yml", [
nearby: ["pond", "lake", "river"],
hasSand: true,
objects: []
},
{
id: "forest",
name: "Forest",
nearby: ["pond", "lake", "beach"],
hasSand: false,
objects: []
}
]);

View File

@ -3,7 +3,9 @@ import "./cli/readline";
import { startFisherTick } from "./fish/fishers";
import { startObjectTimers } from "./fish/locations";
import { initTree } from "./fish/tree";
import { loadDefaultBehaviors } from "./items/behavior/defaults";
startObjectTimers();
await startFisherTick();
await initTree();
loadDefaultBehaviors();

View File

@ -0,0 +1,13 @@
import { addItemBehavior } from ".";
import { fish } from "./items/fish";
import { kekklefruit } from "./items/kekklefruit";
export function loadDefaultBehaviors() {
const list: IBehaviorDefinition[] = [fish, kekklefruit];
for (const item of list) {
for (const key of Object.keys(item.bhv)) {
addItemBehavior(item.id, key, item.bhv[key]);
}
}
}

View File

@ -1,5 +1,26 @@
export const itemBehaviorMap: TItemBehaviorMap = {};
export const itemBehaviorMap: TBehaviorMap = {};
export function addItemBehavior(itemID: string, bhv: TItemBehavior) {
itemBehaviorMap[itemID] = bhv;
export function addItemBehavior(
itemID: string,
bhvID: string,
bhv: TBehaviorCallback
) {
if (!itemBehaviorMap[itemID]) itemBehaviorMap[itemID] = {};
itemBehaviorMap[itemID][bhvID] = bhv;
}
export async function runBehavior(
itemID: string,
bhvID: string,
obj: IObject,
props: IContextProps
): Promise<IBehaviorResponse> {
const callback = itemBehaviorMap[itemID][bhvID];
if (!callback)
return {
success: false,
err: "No callback",
shouldRemove: false
};
return await callback(obj, props);
}

View File

@ -0,0 +1,29 @@
import { addBack } from "@server/backs";
import { CosmicColor } from "@util/CosmicColor";
export const fish: IBehaviorDefinition = {
id: "fish",
bhv: {
async eat(obj, props) {
const color = new CosmicColor(
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255)
);
addBack(props.id, {
m: "color",
id: props.part.id,
color: color.toHexa()
});
return {
success: true,
shouldRemove: true,
and: `and it made him/her turn ${color
.getName()
.toLowerCase()}.`
};
}
}
};

View File

@ -0,0 +1,16 @@
import { incrementFishingChance } from "@server/fish/fishers";
export const kekklefruit: IBehaviorDefinition = {
id: "kekklefruit",
bhv: {
async eat(obj, props) {
const test = await incrementFishingChance(props.user.id);
return {
success: true,
shouldRemove: true,
and: "and got a temporary fishing boost."
};
}
}
};

43
src/util/types.d.ts vendored
View File

@ -8,7 +8,7 @@ interface ICommandResponse {
response: string;
}
type TCommandCallback<User> = (props: {
interface IContextProps {
id: string;
command: string;
args: string[];
@ -16,7 +16,9 @@ type TCommandCallback<User> = (props: {
part: IPart;
user: User;
isDM: boolean;
}) => Promise<string | void>;
}
type TCommandCallback<User> = (props: IContextProps) => Promise<string | void>;
interface CountComponent {
count: number;
@ -112,19 +114,32 @@ interface TFisher {
type TPokedex = IPokemon[];
type TBehavior<T> = () => Promise<T>;
type TBehaviorMap<T> = Record<T, TBehavior>;
interface IItemBehaviorData {
status: boolean;
text: string;
userID: string;
}
type TItemBehavior = Behavior<IItemBehaviorData>;
type TItemBehaviorMap = TBehaviorMap<TItemBehavior>;
interface IGroup {
id: string;
permissions: string[];
}
interface IBehaviorResponse {
success: boolean;
err?: string;
shouldRemove: boolean;
and?: string;
}
type TBehaviorCallback = (
obj: IObject,
props: IContextProps
) => Promise<IBehaviorResponse>;
type TBehavior = Record<string, TBehaviorCallback>;
type TBehaviorMap = Record<string, TBehavior>;
interface IBehaviorDefinition {
id: string;
bhv: TBehavior;
}
interface IFishingChance {
chance: number;
t: number;
}