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

404 lines
8.7 KiB
TypeScript

import { AutocompleteInteraction } from 'discord.js';
import { CustomItem, ItemInventory, db } from '../db';
import { MAX_HEALTH } from './pvp';
export type DefaultItem = Omit<CustomItem, 'guild'>; // uses negative IDs
export type Item = DefaultItem | CustomItem;
export interface Items {
item: Item,
quantity: number
}
export enum DefaultItems {
COIN = 1,
WORKBENCH = 2,
PEBBLE = 3,
TWIG = 4,
APPLE = 5,
BERRIES = 6,
LOG = 7,
AXE = 8,
BLOOD = 9,
BAIT = 10,
FISHING_ROD = 11,
CARP = 12,
PUFFERFISH = 13,
EXOTIC_FISH = 14,
SHOVEL = 15,
DIRT = 16,
MINESHAFT = 17,
PICKAXE = 18,
IRON_PICKAXE = 19,
COAL = 20,
IRON_ORE = 21,
IRON_INGOT = 22,
DIAMOND = 23,
RUBY = 24,
EMERALD = 25,
FURNACE = 26,
FRIED_FISH = 27,
}
export const defaultItems: DefaultItem[] = [
{
id: -1,
name: 'Coin',
emoji: '🪙',
type: 'plain',
maxStack: 9999,
untradable: false
},
{
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
},
{
id: -4,
name: 'Twig',
description: 'Just a tiny bit of wood',
emoji: '🌿',
type: 'plain',
maxStack: 64,
untradable: false
},
{
id: -5,
name: 'Apple',
description: 'A forager\'s snack',
emoji: '🍎',
type: 'consumable',
maxStack: 16,
untradable: false
},
{
id: -6,
name: 'Berries',
description: 'A little treat for the road!',
emoji: '🍓',
type: 'consumable',
maxStack: 16,
untradable: false
},
{
id: -7,
name: 'Log',
description: '㏒',
emoji: '🪵',
type: 'plain',
maxStack: 64,
untradable: false
},
{
id: -8,
name: 'Axe',
description: 'You could chop trees with this. Or commit murder! The choice is up to you',
emoji: '🪓',
type: 'weapon',
maxStack: 1,
untradable: false
},
{
id: -9,
name: 'Blood',
description: 'ow',
emoji: '🩸',
type: 'plain',
maxStack: MAX_HEALTH,
untradable: false
},
{
id: -10,
name: 'Bait',
description: 'I guess you could eat this.',
emoji: '🪱',
type: 'consumable',
maxStack: 128,
untradable: false
},
{
id: -11,
name: 'Fishing Rod',
description: 'Give a man a fish, and he will eat for a day',
emoji: '🎣',
type: 'plain',
maxStack: 1,
untradable: false
},
{
id: -12,
name: 'Carp',
description: 'wow',
emoji: '🐟️',
type: 'plain',
maxStack: 16,
untradable: false
},
{
id: -13,
name: 'Pufferfish',
description: 'yummy!',
emoji: '🐡',
type: 'plain',
maxStack: 16,
untradable: false
},
{
id: -14,
name: 'Exotic Fish',
description: 'lucky!',
emoji: '🐠',
type: 'plain',
maxStack: 16,
untradable: false,
},
{
id: -15,
name: 'Shovel',
description: 'Did you know there\'s no shovel emoji',
emoji: '♠️',
type: 'plain',
maxStack: 1,
untradable: false,
},
{
id: -16,
name: 'Dirt',
description: 'https://media.discordapp.net/attachments/819472665291128873/1081454188325785650/ezgif-2-5ccc7dedf8.gif',
emoji: '🟫',
type: 'consumable',
maxStack: 64,
untradable: false,
},
{
id: -17,
name: 'Mineshaft',
description: 'A place for you to mine ores and minerals!',
emoji: '⛏️',
type: 'plain',
maxStack: 1,
untradable: true
},
{
id: -18,
name: 'Pickaxe',
description: 'Basic mining equipment',
emoji: '⛏️',
type: 'plain',
maxStack: 8,
untradable: false
},
{
id: -19,
name: 'Iron Pickaxe',
description: 'More durable and strong mining equipment',
emoji: '⚒️',
type: 'plain',
maxStack: 8,
untradable: false
},
{
id: -20,
name: 'Coal',
description: 'Fuel, NOT EDIBLE',
emoji: '◾️',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -21,
name: 'Iron Ore',
description: 'Unsmelted iron',
emoji: '◽️',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -22,
name: 'Iron Ingot',
description: 'A sturdy material',
emoji: '◻️',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -23,
name: 'Diamond',
description: 'Shiny rock!',
emoji: '💎',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -24,
name: 'Ruby',
description: 'Reference to the progarmiing......g.',
emoji: '🔻',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -25,
name: 'Emerald',
description: 'A currency in some other world',
emoji: '🟩',
type: 'plain',
maxStack: 128,
untradable: false
},
{
id: -26,
name: 'Furnace',
description: 'A smeltery for your own needs',
emoji: '🔥',
type: 'plain',
maxStack: 1,
untradable: false
},
{
id: -27,
name: 'Fried Fish',
description: 'A very nice and filling meal',
emoji: '🍱',
type: 'consumable',
maxStack: 16,
untradable: false
},
];
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<Item | undefined> {
if (id >= 0) {
return await getCustomItem(id);
} else {
return getDefaultItem(id);
}
}
export async function getCustomItem(id: number) {
return await db<CustomItem>('customItems')
.where('id', id)
.first();
}
export async function getItemQuantity(user: string, itemID: number): Promise<ItemInventory> {
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): Promise<ItemInventory> {
const storedItem = await db<ItemInventory>('itemInventories')
.where('user', user)
.where('item', item.id)
.first();
let inv;
if (storedItem) {
if (storedItem.quantity + quantity === 0) {
await db<ItemInventory>('itemInventories')
.delete()
.where('user', user)
.where('item', item.id);
return {
user: user,
item: item.id,
quantity: 0
};
}
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];
}
export function getMaxStack(item: Item) {
return item.type === 'weapon' ? 1 : item.maxStack;
}
export function formatItem(item: Item | undefined, disableBold = false) {
if (!item) return disableBold ? '? MISSINGNO' : '? **MISSINGNO**';
return disableBold ? `${item.emoji} ${item.name}` : `${item.emoji} **${item.name}**`;
}
export function formatItems(item: Item | undefined, quantity: number, disableBold = false) {
return `${quantity}x ${formatItem(item, disableBold)}`;
}
export function formatItemsArray(items: Items[], disableBold = false) {
if (items.length === 0) return disableBold ? 'nothing' : '**nothing**';
return items.map(i => formatItems(i.item, i.quantity, disableBold)).join(' ');
}
function createItemAutocomplete(onlyCustom: boolean) {
return async (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()));
let items;
if (onlyCustom) {
items = customItems;
} else {
items = [...foundDefaultItems, ...customItems];
}
await interaction.respond(
items.map(choice => ({ name: `${choice.emoji} ${choice.name}`, value: choice.id.toString() }))
);
};
}
export const itemAutocomplete = createItemAutocomplete(false);
export const customItemAutocomplete = createItemAutocomplete(true);