jillo-bot/src/lib/rpg/items.ts

232 lines
6 KiB
TypeScript
Raw Normal View History

2023-11-15 11:03:01 +01:00
import { AutocompleteInteraction, User } from 'discord.js';
2023-11-15 11:57:20 +01:00
import { CustomItem, ItemInventory, db } from '../db';
2023-11-15 01:40:57 +01:00
type DefaultItem = Omit<CustomItem, 'guild'>; // uses negative IDs
type Item = DefaultItem | CustomItem;
interface Behavior {
name: string,
description: string,
itemType: 'plain' | 'weapon' | 'consumable',
// triggers upon use
// for 'weapons', this is on hit
// for 'consumable', this is on use
// for 'plain', ...??
// returns `true` upon success, `false` otherwise
action?: (item: Item, user: User) => Promise<boolean>
}
2023-11-15 11:56:46 +01:00
enum DefaultItems {
COIN = 1,
WORKBENCH = 2,
PEBBLE = 3
}
2023-11-15 01:40:57 +01:00
export const defaultItems: DefaultItem[] = [
{
'id': -1,
'name': 'Coin',
'emoji': '🪙',
'type': 'plain',
'maxStack': 9999,
'untradable': false
2023-11-15 11:56:46 +01:00
},
{
'id': -2,
'name': 'Workbench',
'description': 'A place for you to work with tools, for simple things',
'emoji': '🛠️',
'type': 'plain',
'maxStack': 1,
'untradable': false
},
{
'id': -3,
'name': 'Pebble',
'description': 'If you get 5 of them you will instantly ! !!!',
'emoji': '🪨',
'type': 'plain',
'maxStack': 64,
'untradable': false
2023-11-15 01:40:57 +01:00
}
];
export const behaviors: Behavior[] = [
{
'name': 'heal',
'description': 'Heals the user by `behaviorValue`',
'itemType': 'consumable',
'action': async (item: Item, user: User) => {
// todo
return false;
}
}
];
2023-11-15 11:56:46 +01:00
interface Items {
item: Item,
quantity: number
}
const defaultFormatRecipe = (inputs: Items[], requirements: Items[], outputs: Items[]) =>
`${formatItemsArray(inputs)}${requirements.length === 0 ? '' : ` w/ ${formatItemsArray(requirements)}`} => ${formatItemsArray(outputs)}`;
interface CraftingStation {
key: string,
name: string,
description: string,
emoji: string,
requires?: Item,
// in seconds
cooldown?: number,
formatRecipe?: (inputs: Items[], requirements: Items[], outputs: Items[]) => string,
manipulateResults?: (outputs: Items[]) => Items[]
}
export const craftingStations: CraftingStation[] = [
{
key: 'forage',
name: 'Forage',
description: 'Pick up various sticks and stones from the forest',
emoji: '🌲',
cooldown: 60 * 5,
formatRecipe: (_inputs, _requirements, outputs) => `${outputs.map(i => formatItems(i.item, i.quantity) + '?').join(' ')}`,
manipulateResults: (outputs) =>
outputs.map(o => ({item: o.item, quantity: Math.floor(o.quantity * Math.random())})).filter(o => o.quantity !== 0)
},
{
key: 'hand',
name: 'Hand',
description: 'You can use your hands to make a small assortment of things',
emoji: '✋'
},
{
key: 'workbench',
name: 'Workbench',
description: 'A place for you to work with tools, for simple things',
emoji: '🛠️',
requires: getDefaultItem(DefaultItems.WORKBENCH)
}
];
interface Recipe {
station: string,
inputs: Items[],
requirements: Items[],
outputs: Items[]
}
export const recipes: Recipe[] = [
{
station: 'forage',
inputs: [],
requirements: [],
outputs: [
{ item: getDefaultItem(DefaultItems.PEBBLE), quantity: 2 }
]
},
{
station: 'workbench',
inputs: [
{ item: getDefaultItem(DefaultItems.PEBBLE), quantity: 2 }
],
requirements: [],
outputs: [
{ item: getDefaultItem(DefaultItems.WORKBENCH), quantity: 1 }
]
}
];
2023-11-15 01:40:57 +01:00
export async function getCustomItem(id: number) {
return await db<CustomItem>('customItems')
.where('id', id)
.first();
}
2023-11-15 11:56:46 +01:00
export function getDefaultItem(id: DefaultItems): Item
export function getDefaultItem(id: number): Item | undefined {
return defaultItems.find(item => Math.abs(item.id) === Math.abs(id));
}
2023-11-15 01:40:57 +01:00
export async function getItem(id: number): Promise<Item | undefined> {
if (id >= 0) {
return await getCustomItem(id);
} else {
2023-11-15 11:56:46 +01:00
return getDefaultItem(id);
2023-11-15 01:40:57 +01:00
}
}
2023-11-15 11:03:01 +01:00
export async function getItemQuantity(user: string, itemID: number) {
return (await db<ItemInventory>('itemInventories')
.where('item', itemID)
.where('user', user)
.first())
|| {
'user': user,
'item': itemID,
'quantity': 0
};
}
export async function giveItem(user: string, item: Item, quantity = 1) {
const storedItem = await db<ItemInventory>('itemInventories')
.where('user', user)
.where('item', item.id)
.first();
let inv;
if (storedItem) {
inv = await db<ItemInventory>('itemInventories')
.update({
'quantity': db.raw('MIN(quantity + ?, ?)', [quantity, getMaxStack(item)])
})
.limit(1)
.where('user', user)
.where('item', item.id)
.returning('*');
} else {
inv = await db<ItemInventory>('itemInventories')
.insert({
'user': user,
'item': Math.min(item.id, getMaxStack(item)),
'quantity': quantity
})
.returning('*');
}
return inv[0];
}
2023-11-15 01:40:57 +01:00
export function getMaxStack(item: Item) {
return item.type === 'weapon' ? 1 : item.maxStack;
}
2023-11-15 11:03:01 +01:00
export function formatItem(item: Item | undefined) {
if (!item) return '? **MISSINGNO**';
2023-11-15 01:40:57 +01:00
return `${item.emoji} **${item.name}**`;
}
2023-11-15 11:03:01 +01:00
export function formatItems(item: Item | undefined, quantity: number) {
2023-11-15 01:40:57 +01:00
return `${quantity}x ${formatItem(item)}`;
2023-11-15 11:03:01 +01:00
}
2023-11-15 11:56:46 +01:00
export function formatItemsArray(items: Items[]) {
return items.map(i => formatItems(i.item, i.quantity)).join(' ');
}
2023-11-15 11:03:01 +01:00
export async function itemAutocomplete(interaction: AutocompleteInteraction) {
const focused = interaction.options.getFocused();
const customItems = await db<CustomItem>('customItems')
.select('emoji', 'name', 'id')
// @ts-expect-error this LITERALLY works
.whereLike(db.raw('UPPER(name)'), `%${focused.toUpperCase()}%`)
.where('guild', interaction.guildId!)
.limit(25);
const foundDefaultItems = defaultItems.filter(item => item.name.toUpperCase().includes(focused.toUpperCase()));
const items = [...foundDefaultItems, ...customItems];
await interaction.respond(
items.map(choice => ({ name: `${choice.emoji} ${choice.name}`, value: choice.id.toString() }))
);
2023-11-15 01:40:57 +01:00
}