import { AutocompleteInteraction, User } from 'discord.js'; import { CustomItem, ItemInventory, db } from '../db'; export type DefaultItem = Omit; // uses negative IDs export type Item = DefaultItem | CustomItem; export 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 } export interface Items { item: Item, quantity: number } const defaultFormatRecipe = (inputs: Items[], requirements: Items[], outputs: Items[]) => `${formatItemsArray(inputs)}${requirements.length === 0 ? '' : ` w/ ${formatItemsArray(requirements)}`} => ${formatItemsArray(outputs)}`; export 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 interface Recipe { station: string, inputs: Items[], requirements: Items[], outputs: Items[] } export async function getCustomItem(id: number) { return await db('customItems') .where('id', id) .first(); } export function getDefaultItem(id: DefaultItems): Item export function getDefaultItem(id: number): Item | undefined { return defaultItems.find(item => Math.abs(item.id) === Math.abs(id)); } export async function getItem(id: number): Promise { if (id >= 0) { return await getCustomItem(id); } else { return getDefaultItem(id); } } export async function getItemQuantity(user: string, itemID: number) { return (await db('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('itemInventories') .where('user', user) .where('item', item.id) .first(); let inv; if (storedItem) { inv = await db('itemInventories') .update({ 'quantity': db.raw('MIN(quantity + ?, ?)', [quantity, getMaxStack(item)]) }) .limit(1) .where('user', user) .where('item', item.id) .returning('*'); } else { inv = await db('itemInventories') .insert({ 'user': user, 'item': Math.min(item.id, getMaxStack(item)), 'quantity': quantity }) .returning('*'); } return inv[0]; } export function getMaxStack(item: Item) { return item.type === 'weapon' ? 1 : item.maxStack; } export function formatItem(item: Item | undefined) { if (!item) return '? **MISSINGNO**'; return `${item.emoji} **${item.name}**`; } export function formatItems(item: Item | undefined, quantity: number) { return `${quantity}x ${formatItem(item)}`; } export function formatItemsArray(items: Items[]) { return items.map(i => formatItems(i.item, i.quantity)).join(' '); } export async function itemAutocomplete(interaction: AutocompleteInteraction) { const focused = interaction.options.getFocused(); const customItems = await db('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() })) ); }