79 lines
3.3 KiB
TypeScript
79 lines
3.3 KiB
TypeScript
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
|
|
import { getItem, getItemQuantity, formatItems, formatItem, weaponInventoryAutocomplete } from '../lib/rpg/items';
|
|
import { Command } from '../types/index';
|
|
import { initHealth, dealDamage, BLOOD_ITEM, BLOOD_ID, resetInvincible, INVINCIBLE_TIMER, getInvincibleMs } from '../lib/rpg/pvp';
|
|
import { getBehavior, getBehaviors } from '../lib/rpg/behaviors';
|
|
import { Right } from '../lib/util';
|
|
|
|
export default {
|
|
data: new SlashCommandBuilder()
|
|
.setName('attack')
|
|
.setDescription('Attack someone using a weapon you have')
|
|
.addStringOption(option =>
|
|
option
|
|
.setName('weapon')
|
|
.setAutocomplete(true)
|
|
.setDescription('The weapon to use')
|
|
.setRequired(true)
|
|
)
|
|
.addUserOption(option =>
|
|
option
|
|
.setName('user')
|
|
.setRequired(true)
|
|
.setDescription('Who to attack with the weapon')
|
|
)
|
|
.setDMPermission(false),
|
|
|
|
execute: async (interaction: CommandInteraction) => {
|
|
if (!interaction.isChatInputCommand()) return;
|
|
|
|
const member = interaction.member! as GuildMember;
|
|
await initHealth(member.id);
|
|
const weaponID = parseInt(interaction.options.getString('weapon', true));
|
|
const target = interaction.options.getUser('user', true);
|
|
|
|
await interaction.deferReply({ephemeral: true});
|
|
|
|
const weapon = await getItem(weaponID);
|
|
if (!weapon) return interaction.followUp('No such item exists!');
|
|
if (weapon.type !== 'weapon') return interaction.followUp('That is not a weapon!');
|
|
const inv = await getItemQuantity(member.id, weapon.id);
|
|
if (inv.quantity === 0) return interaction.followUp('You do not have this weapon!');
|
|
const invinTimer = await getInvincibleMs(target.id);
|
|
if (invinTimer > 0) return interaction.followUp(`You can only attack this user <t:${Math.floor((Date.now() + invinTimer) / 1000)}:R> (or if they perform an action first)!`);
|
|
|
|
let dmg = weapon.maxStack;
|
|
const messages = [];
|
|
|
|
const behaviors = await getBehaviors(weapon);
|
|
for (const itemBehavior of behaviors) {
|
|
const behavior = getBehavior(itemBehavior.behavior);
|
|
if (!behavior) continue;
|
|
if (!behavior.onAttack) continue;
|
|
const res = await behavior.onAttack({
|
|
value: itemBehavior.value,
|
|
damage: dmg,
|
|
item: weapon,
|
|
user: member.id,
|
|
target: target.id,
|
|
});
|
|
if (res instanceof Right) {
|
|
await interaction.followUp(`You tried to attack with ${formatItem(weapon)}... but failed!\n${res.getValue()}`);
|
|
return;
|
|
} else {
|
|
const { message, damage } = res.getValue();
|
|
if (message) messages.push(message);
|
|
if (damage) dmg = damage;
|
|
}
|
|
}
|
|
|
|
await dealDamage(target.id, dmg);
|
|
const newHealth = await getItemQuantity(target.id, BLOOD_ID);
|
|
|
|
if (target.id !== member.id) await resetInvincible(member.id);
|
|
|
|
await interaction.followUp(`You hit ${target} with ${formatItem(weapon)} for ${BLOOD_ITEM.emoji} **${dmg}** damage!\n${messages.map(m => `_${m}_\n`).join('')}They are now at ${formatItems(BLOOD_ITEM, newHealth.quantity)}.\nYou can attack them again <t:${Math.floor((Date.now() + INVINCIBLE_TIMER) / 1000)}:R> (or if they perform an action first).`);
|
|
},
|
|
|
|
autocomplete: weaponInventoryAutocomplete,
|
|
} satisfies Command; |