2023-11-16 11:33:11 +01:00
import { AutocompleteInteraction , GuildMember , CommandInteraction , SlashCommandBuilder } from 'discord.js' ;
2023-11-18 13:24:56 +01:00
import { CraftingStationCooldown , CustomCraftingRecipe , db } from '../lib/db' ;
2023-11-15 22:01:55 +01:00
import { getStation , canUseStation , craftingStations , verb , CraftingStation } from '../lib/rpg/craftingStations' ;
2023-11-15 16:11:48 +01:00
import { formatItem , getItemQuantity , formatItems , getMaxStack , giveItem , formatItemsArray } from '../lib/rpg/items' ;
2023-11-18 13:24:56 +01:00
import { getRecipe , defaultRecipes , formatRecipe , resolveCustomRecipe } from '../lib/rpg/recipes' ;
2023-11-16 11:33:11 +01:00
import { Command } from '../types/index' ;
2023-11-21 21:28:32 +01:00
import { initHealth , resetInvincible } from '../lib/rpg/pvp' ;
2023-11-15 16:04:36 +01:00
2023-11-16 11:33:11 +01:00
export default {
2023-11-15 16:04:36 +01:00
data : new SlashCommandBuilder ( )
. setName ( 'craft' )
. setDescription ( 'Craft an item with items you have' )
. addStringOption ( option = >
option
. setName ( 'station' )
. setAutocomplete ( true )
. setDescription ( 'Which station to use' )
. setRequired ( true )
)
. addStringOption ( option = >
option
. setName ( 'recipe' )
. setAutocomplete ( true )
. setDescription ( 'What to craft' )
. setRequired ( true )
)
. setDMPermission ( false ) ,
2023-11-16 11:33:11 +01:00
execute : async ( interaction : CommandInteraction ) = > {
2023-11-15 16:04:36 +01:00
if ( ! interaction . isChatInputCommand ( ) ) return ;
2023-11-16 11:33:11 +01:00
const member = interaction . member ! as GuildMember ;
2023-11-21 20:17:15 +01:00
await initHealth ( member . id ) ;
2023-11-15 16:04:36 +01:00
const recipeID = parseInt ( interaction . options . getString ( 'recipe' , true ) ) ;
await interaction . deferReply ( { ephemeral : true } ) ;
2023-11-18 13:24:56 +01:00
const recipe = await getRecipe ( recipeID ) ;
2023-11-15 16:04:36 +01:00
if ( ! recipe ) return interaction . followUp ( 'Recipe does not exist!' ) ;
const station = getStation ( recipe . station ) ! ;
if ( ! canUseStation ( member . id , station ) ) return interaction . followUp ( ` ${ station . emoji } You need ${ formatItem ( station . requires ) } to use this station! ` ) ;
for ( const input of recipe . inputs ) {
const inv = await getItemQuantity ( member . id , input . item . id ) ;
if ( inv . quantity < input . quantity ) return interaction . followUp ( ` You need ${ formatItems ( input . item , input . quantity ) } for this recipe! (You have ${ formatItems ( input . item , inv . quantity ) } ) ` ) ;
}
for ( const req of recipe . requirements ) {
const inv = await getItemQuantity ( member . id , req . item . id ) ;
if ( inv . quantity < req . quantity ) return interaction . followUp ( ` You need ${ formatItems ( req . item , req . quantity ) } to begin this recipe! (You have ${ formatItems ( req . item , inv . quantity ) } . Don't worry, these items will not be consumed.) ` ) ;
}
for ( const out of recipe . outputs ) {
const inv = await getItemQuantity ( member . id , out . item . id ) ;
2023-11-15 17:09:15 +01:00
if ( inv . quantity + out . quantity > getMaxStack ( out . item ) ) return interaction . followUp ( ` You do not have enough inventory storage for this recipe! ( ${ formatItems ( out . item , inv . quantity + out . quantity ) } is bigger than the stack size of ${ getMaxStack ( out . item ) } x.) ` ) ;
2023-11-15 16:04:36 +01:00
}
let cooldown ;
if ( station . cooldown ) {
cooldown = await db < CraftingStationCooldown > ( 'craftingStationCooldowns' )
. where ( 'station' , station . key )
. where ( 'user' , member . id )
. first ( ) ;
if ( cooldown && ( cooldown . usedAt + station . cooldown * 1000 ) > Date . now ( ) )
return interaction . followUp ( ` ${ station . emoji } You can use this station again <t: ${ Math . floor ( cooldown . usedAt / 1000 + station . cooldown ) } :R>! ` ) ;
}
// proceed with crafting!
for ( const input of recipe . inputs ) {
giveItem ( member . id , input . item , - input . quantity ) ;
}
const outputs = station . manipulateResults ? station . manipulateResults ( recipe . outputs ) : recipe . outputs ;
for ( const output of outputs ) {
giveItem ( member . id , output . item , output . quantity ) ;
}
let nextUsableAt ;
if ( station . cooldown ) {
if ( ! cooldown ) {
await db < CraftingStationCooldown > ( 'craftingStationCooldowns' )
. insert ( {
station : station.key ,
user : member.id ,
2023-11-15 16:22:57 +01:00
usedAt : Date.now ( )
2023-11-15 16:04:36 +01:00
} ) ;
} else {
await db < CraftingStationCooldown > ( 'craftingStationCooldowns' )
. where ( 'station' , station . key )
. where ( 'user' , member . id )
. update ( {
2023-11-15 16:22:57 +01:00
usedAt : Date.now ( )
2023-11-15 16:04:36 +01:00
} ) ;
}
nextUsableAt = Date . now ( ) + station . cooldown * 1000 ;
}
2023-11-21 21:28:32 +01:00
await resetInvincible ( member . id ) ;
2023-11-15 22:01:55 +01:00
return interaction . followUp ( ` ${ station . emoji } ${ verb ( station ) } ${ formatItemsArray ( outputs ) } ! ${ outputs . length === 1 ? ` \ n_ ${ outputs [ 0 ] . item . description } _ ` : '' } ${ nextUsableAt ? ` \ n ${ station . name } usable again <t: ${ Math . floor ( nextUsableAt / 1000 ) } :R> ` : '' } ` ) ;
2023-11-15 16:04:36 +01:00
} ,
autocomplete : async ( interaction : AutocompleteInteraction ) = > {
const focused = interaction . options . getFocused ( true ) ;
if ( focused . name === 'station' ) {
2023-11-15 22:01:55 +01:00
const found = ( await Promise . all (
craftingStations
. map ( async station = > [ station , await canUseStation ( interaction . user . id , station ) ] )
) )
// eslint-disable-next-line @typescript-eslint/no-unused-vars
. filter ( ( [ _station , usable ] ) = > usable )
// eslint-disable-next-line @typescript-eslint/no-unused-vars
. map ( ( [ station , _ ] ) = > station as CraftingStation )
2023-11-15 16:04:36 +01:00
. filter ( station = > station . name . toLowerCase ( ) . includes ( focused . value . toLowerCase ( ) ) )
. map ( station = > ( {
name : ` ${ station . emoji } ${ station . name } ` ,
value : station.key
} ) ) ;
return interaction . respond ( found ) ;
} else if ( focused . name === 'recipe' ) {
2023-11-18 13:24:56 +01:00
const station = interaction . options . getString ( 'station' ) ;
2023-11-15 16:04:36 +01:00
2023-11-18 13:24:56 +01:00
const foundDefaultRecipes = defaultRecipes
. filter ( recipe = > recipe . station === station )
. filter ( recipe = > recipe . outputs . filter ( n = > n . item . name . toLowerCase ( ) . includes ( focused . value . toLowerCase ( ) ) ) . length > 0 ) ;
const customRecipes = await db < CustomCraftingRecipe > ( 'customCraftingRecipes' )
. where ( 'station' , station ) ;
const resolvedCustomRecipes = await Promise . all ( customRecipes . map ( resolveCustomRecipe ) ) ;
const foundCustomRecipes = resolvedCustomRecipes
. filter ( recipe = > recipe . outputs . filter ( n = > n . item . name . toLowerCase ( ) . includes ( focused . value . toLowerCase ( ) ) ) . length > 0 ) ;
const recipes = [ . . . foundDefaultRecipes , . . . foundCustomRecipes ] ;
return interaction . respond (
recipes
. map ( recipe = > ( {
name : formatRecipe ( recipe , true ) ,
value : recipe.id.toString ( )
} ) )
) ;
2023-11-15 16:04:36 +01:00
}
}
2023-11-16 11:33:11 +01:00
} satisfies Command ;