const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const { getServerMode, getEmbedColor, EMBED_COLORS } = require('../utils/modeHelper'); const { updateStandaloneXp } = require('../utils/standaloneXp'); const activeHeists = new Map(); const TARGETS = [ { name: 'Corner Store', emoji: '🏪', difficulty: 0.7, minReward: 20, maxReward: 50 }, { name: 'Gas Station', emoji: '⛽', difficulty: 0.6, minReward: 30, maxReward: 70 }, { name: 'Jewelry Store', emoji: '💎', difficulty: 0.5, minReward: 50, maxReward: 120 }, { name: 'Bank', emoji: '🏦', difficulty: 0.4, minReward: 80, maxReward: 200 }, { name: 'Casino', emoji: '🎰', difficulty: 0.3, minReward: 100, maxReward: 300 }, { name: 'Federal Reserve', emoji: '🏛️', difficulty: 0.2, minReward: 150, maxReward: 500 }, ]; module.exports = { data: new SlashCommandBuilder() .setName('heist') .setDescription('Start a group heist!') .addStringOption(option => option.setName('target') .setDescription('Choose your heist target') .setRequired(true) .addChoices( { name: '🏪 Corner Store (Easy)', value: '0' }, { name: '⛽ Gas Station', value: '1' }, { name: '💎 Jewelry Store', value: '2' }, { name: '🏦 Bank (Medium)', value: '3' }, { name: '🎰 Casino', value: '4' }, { name: '🏛️ Federal Reserve (Hard)', value: '5' } ) ), async execute(interaction, supabase, client) { const targetIndex = parseInt(interaction.options.getString('target')); const target = TARGETS[targetIndex]; const leader = interaction.user; const mode = await getServerMode(supabase, interaction.guildId); const guildId = interaction.guildId; const heistKey = `${guildId}-heist`; if (activeHeists.has(heistKey)) { return interaction.reply({ content: 'There\'s already an active heist in this server! Wait for it to finish.', ephemeral: true }); } const participants = new Set([leader.id]); activeHeists.set(heistKey, { target, leader: leader.id, participants, usernames: { [leader.id]: leader.username } }); const embed = new EmbedBuilder() .setColor(getEmbedColor(mode)) .setTitle(`${target.emoji} Heist: ${target.name}`) .setDescription(`${leader} is planning a heist!\n\nClick **Join Heist** to participate.\nMore participants = higher success chance!`) .addFields( { name: '🎯 Target', value: target.name, inline: true }, { name: '💰 Reward', value: `${target.minReward}-${target.maxReward} XP each`, inline: true }, { name: '📊 Base Success', value: `${Math.round(target.difficulty * 100)}%`, inline: true }, { name: '👥 Participants', value: `1. ${leader.username}` } ) .setFooter({ text: 'Heist starts in 60 seconds!' }) .setTimestamp(); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId(`heist_join_${guildId}`) .setLabel('Join Heist') .setStyle(ButtonStyle.Primary) .setEmoji('🔫'), new ButtonBuilder() .setCustomId(`heist_start_${guildId}`) .setLabel('Start Now') .setStyle(ButtonStyle.Success) .setEmoji('🚀') ); const message = await interaction.reply({ embeds: [embed], components: [row], fetchReply: true }); const collector = message.createMessageComponentCollector({ filter: i => i.customId.startsWith('heist_'), time: 60000 }); collector.on('collect', async (i) => { const heistData = activeHeists.get(heistKey); if (!heistData) return; if (i.customId.startsWith('heist_join')) { if (heistData.participants.has(i.user.id)) { return i.reply({ content: 'You\'re already in this heist!', ephemeral: true }); } heistData.participants.add(i.user.id); heistData.usernames[i.user.id] = i.user.username; const participantList = Array.from(heistData.participants) .map((id, idx) => `${idx + 1}. ${heistData.usernames[id]}`) .join('\n'); const bonusChance = (heistData.participants.size - 1) * 5; const totalChance = Math.min(95, Math.round(target.difficulty * 100) + bonusChance); embed.spliceFields(3, 1, { name: '👥 Participants', value: participantList }); embed.spliceFields(2, 1, { name: '📊 Success Chance', value: `${totalChance}%`, inline: true }); await i.update({ embeds: [embed] }); } if (i.customId.startsWith('heist_start') && i.user.id === heistData.leader) { collector.stop('started'); } }); collector.on('end', async (collected, reason) => { const heistData = activeHeists.get(heistKey); if (!heistData) return; activeHeists.delete(heistKey); const participantCount = heistData.participants.size; const bonusChance = (participantCount - 1) * 5; const successChance = Math.min(95, target.difficulty * 100 + bonusChance) / 100; const success = Math.random() < successChance; if (success) { const baseReward = Math.floor(Math.random() * (target.maxReward - target.minReward + 1)) + target.minReward; const teamBonus = Math.floor(baseReward * (participantCount - 1) * 0.1); const totalReward = baseReward + teamBonus; for (const participantId of heistData.participants) { if (mode === 'standalone') { await updateStandaloneXp(supabase, participantId, guildId, totalReward, heistData.usernames[participantId]); } else if (supabase) { try { const { data: profile } = await supabase .from('user_profiles') .select('xp') .eq('discord_id', participantId) .maybeSingle(); if (profile) { await supabase .from('user_profiles') .update({ xp: (profile.xp || 0) + totalReward }) .eq('discord_id', participantId); } } catch (e) {} } } const participantMentions = Array.from(heistData.participants) .map(id => `<@${id}>`) .join(', '); const successEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.success) .setTitle(`${target.emoji} Heist Successful!`) .setDescription(`The crew pulled off the ${target.name} heist!`) .addFields( { name: '👥 Crew', value: participantMentions }, { name: '💰 Each Earned', value: `${totalReward} XP` } ) .setTimestamp(); await interaction.editReply({ embeds: [successEmbed], components: [] }); } else { const failEmbed = new EmbedBuilder() .setColor(EMBED_COLORS.error) .setTitle(`${target.emoji} Heist Failed!`) .setDescription(`The ${target.name} heist went wrong! The crew escaped empty-handed.`) .addFields({ name: '👥 Crew', value: Array.from(heistData.participants).map(id => heistData.usernames[id]).join(', ') }) .setTimestamp(); await interaction.editReply({ embeds: [failEmbed], components: [] }); } }); }, };