const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const { getServerMode, getEmbedColor, EMBED_COLORS } = require('../utils/modeHelper'); const { updateStandaloneXp } = require('../utils/standaloneXp'); const activeDuels = new Map(); module.exports = { data: new SlashCommandBuilder() .setName('duel') .setDescription('Challenge someone to a duel!') .addUserOption(option => option.setName('opponent') .setDescription('The user to challenge') .setRequired(true) ) .addIntegerOption(option => option.setName('bet') .setDescription('XP to bet (0-100)') .setRequired(false) .setMinValue(0) .setMaxValue(100) ), async execute(interaction, supabase, client) { const opponent = interaction.options.getUser('opponent'); const bet = interaction.options.getInteger('bet') || 0; const challenger = interaction.user; const mode = await getServerMode(supabase, interaction.guildId); const guildId = interaction.guildId; if (opponent.id === challenger.id) { return interaction.reply({ content: "You can't duel yourself!", ephemeral: true }); } if (opponent.bot) { return interaction.reply({ content: "You can't duel bots!", ephemeral: true }); } const duelKey = `${guildId}-${challenger.id}`; if (activeDuels.has(duelKey)) { return interaction.reply({ content: 'You already have an active duel!', ephemeral: true }); } const embed = new EmbedBuilder() .setColor(getEmbedColor(mode)) .setTitle('⚔️ Duel Challenge!') .setDescription(`${challenger} challenges ${opponent} to a duel!`) .addFields( { name: '💰 Bet', value: bet > 0 ? `${bet} XP` : 'No bet', inline: true }, { name: '⏱️ Expires', value: 'In 60 seconds', inline: true } ) .setTimestamp(); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId(`duel_accept_${challenger.id}_${opponent.id}`) .setLabel('Accept') .setStyle(ButtonStyle.Success), new ButtonBuilder() .setCustomId(`duel_decline_${challenger.id}_${opponent.id}`) .setLabel('Decline') .setStyle(ButtonStyle.Danger) ); const message = await interaction.reply({ content: `${opponent}`, embeds: [embed], components: [row], fetchReply: true }); activeDuels.set(duelKey, { opponent: opponent.id, bet }); const collector = message.createMessageComponentCollector({ filter: i => i.user.id === opponent.id && i.customId.includes(challenger.id), time: 60000, max: 1 }); collector.on('collect', async (i) => { activeDuels.delete(duelKey); if (i.customId.startsWith('duel_decline')) { const declineEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.error) .setTitle('⚔️ Duel Declined') .setDescription(`${opponent} declined the duel challenge.`) .setTimestamp(); await i.update({ embeds: [declineEmbed], components: [] }); return; } const challengerRoll = Math.floor(Math.random() * 100) + 1; const opponentRoll = Math.floor(Math.random() * 100) + 1; let winner, loser, winnerRoll, loserRoll; if (challengerRoll > opponentRoll) { winner = challenger; loser = opponent; winnerRoll = challengerRoll; loserRoll = opponentRoll; } else if (opponentRoll > challengerRoll) { winner = opponent; loser = challenger; winnerRoll = opponentRoll; loserRoll = challengerRoll; } else { const tieEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.warning) .setTitle('⚔️ It\'s a Tie!') .setDescription(`Both rolled **${challengerRoll}**! Nobody wins.`) .addFields( { name: `${challenger.username}`, value: `🎲 ${challengerRoll}`, inline: true }, { name: `${opponent.username}`, value: `🎲 ${opponentRoll}`, inline: true } ) .setTimestamp(); await i.update({ embeds: [tieEmbed], components: [] }); return; } if (bet > 0) { if (mode === 'standalone') { await updateStandaloneXp(supabase, winner.id, guildId, bet, winner.username); } else if (supabase) { try { const { data: winnerProfile } = await supabase .from('user_profiles') .select('xp') .eq('discord_id', winner.id) .maybeSingle(); if (winnerProfile) { await supabase .from('user_profiles') .update({ xp: (winnerProfile.xp || 0) + bet }) .eq('discord_id', winner.id); } } catch (e) {} } } const resultEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.success) .setTitle('⚔️ Duel Results!') .setDescription(`🏆 **${winner.username}** wins the duel!`) .addFields( { name: `${challenger.username}`, value: `🎲 Rolled: **${challengerRoll}**`, inline: true }, { name: `${opponent.username}`, value: `🎲 Rolled: **${opponentRoll}**`, inline: true } ) .setTimestamp(); if (bet > 0) { resultEmbed.addFields({ name: '💰 Reward', value: `${winner.username} wins **${bet} XP**!` }); } await i.update({ embeds: [resultEmbed], components: [] }); }); collector.on('end', async (collected) => { if (collected.size === 0) { activeDuels.delete(duelKey); const timeoutEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.warning) .setTitle('⚔️ Duel Expired') .setDescription(`${opponent} didn't respond in time.`) .setTimestamp(); await interaction.editReply({ embeds: [timeoutEmbed], components: [] }); } }); }, };