package net.uomc.mineshaft; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; import org.json.JSONObject; import com.mouldycheerio.dbot.CustomBot; import com.mouldycheerio.dbot.commands.CommandDetails; import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand; import com.mouldycheerio.dbot.util.EventWaiter; import com.mouldycheerio.dbot.util.PeelingUtils; import com.vdurmont.emoji.EmojiParser; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.uomc.mineshaft.resources.ResourceManager; public class MineCommand extends CooldownCommand { private static final String DROP_END_MESSAGE2 = "Too late!"; private static final String DROP_END_MESSAGE = "The ore has disappeared!"; private static final long DROP_DURATION_SECONDS = 90; private static final String DROP_TITLE = "An ore has spawned!"; private static final String DROP_DESCRIPTION = "Say `%s` to mine it!\nHurry though, it will be gone %s"; private static final double COAL_SPAWN_AMOUNT = 64; private static final double IRON_SPAWN_AMOUNT = 8; private static final double COPPER_SPAWN_AMOUNT = 8; private static final double LAPIS_SPAWN_AMOUNT = 16; private static final double REDSTONE_SPAWN_AMOUNT = 16; private static final double GOLD_SPAWN_AMOUNT = 8; private static final double EMERALD_SPAWN_AMOUNT = 1; private static final double DIAMOND_SPAWN_AMOUNT = 1; private static final double OBSIDIAN_SPAWN_AMOUNT = 1; private static final double XP_SPAWN_AMOUNT = 1000; private static final double QUARTZ_SPAWN_AMOUNT = 16; private static final double SKULL_SPAWN_AMOUNT = 1; private static final double NETHERITE_SPAWN_AMOUNT = 1; private Map ores; private List oresList; private List netherOresList; private Mineshaft bot; protected MineCommand(Mineshaft bot) { super(bot); this.bot = bot; setCooldown(15l * 1000l); setDetails(CommandDetails.from("mine", "mine diamonds")); this.ores = loadOreImages(); oresList = loadOreOrder(); netherOresList = loadNetherOreOrder(); } public List loadOreOrder() { return List.of( "coal", "copper", "iron", "lapis", "redstone", "gold", "diamond", "emerald", "obsidian", "obsidian" ); } public List loadNetherOreOrder() { return List.of( "quartz", "quartz", "quartz", "quartz", "gold", "gold", "skull", "skull", "netherite", "netherite" ); } private int levelToMaxOre(int pickaxe) { switch (pickaxe) { case 0: // stone pickaxe return 2; // copper case 1: // copper pickaxe return 4; // lapis case 2: // iron pickaxe return 6; // gold case 3: // gold pickaxe return 8; // diamond case 4: // diamond pickaxe return 9; // obsidian case 5: // netherite pickaxe return 10; // obsidian, 2 } return 9; } public Map loadOreImages() { Map ores = new LinkedHashMap(); JSONObject config = bot.getConfig(); if (!config.has("mineshaft")) return ores; JSONObject mineshaftConfig = config.getJSONObject("mineshaft"); if (!mineshaftConfig.has("ores")) return ores; JSONObject oresConfig = mineshaftConfig.getJSONObject("ores"); oresConfig.keySet().forEach(k -> ores.put(k, oresConfig.getString(k))); return ores; } public MineshaftItem getOre(Member m) { return getOre(m, false); } public MineshaftItem getOre(Member m, boolean nether) { int pickaxe = bot.getPickaxes().getPickaxeLevel(m); double random = Math.random(); int maxOre = levelToMaxOre(pickaxe); List list = oresList; if(nether) list = netherOresList; double c = getEfficiencyCurve(m); int i = (int) Math.floor(Math.pow(random, c) * maxOre); String oreName = list.get(i); MineshaftItem ore = MineshaftItem.valueOf(oreName.toUpperCase()); return ore; } @Override public boolean trigger(MessageReceivedEvent e) { boolean nether = bot.getPickaxes().isNether(e.getMember()); MineshaftItem ore = getOre(e.getMember(), nether); int i; if (nether) { i = oresList.indexOf(ore.toString().toLowerCase()); } else { i = netherOresList.indexOf(ore.toString().toLowerCase()); } spawnDrop(e.getChannel(), ore, i, nether, m -> {}); e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(":pick:"))).queue(); return true; } public void spawnDrop(MessageChannel channel, MineshaftItem ore, int oreLevel, boolean nether, Consumer then) { sendDrop(channel, ore, nether, m -> { EventWaiter eventWaiter = new EventWaiter(); channel.getJDA().addEventListener(eventWaiter); eventWaiter.waitForEvent(MessageReceivedEvent.class, e -> { if (!e.getMessage().getContentRaw().equalsIgnoreCase(getPickString(ore))) return false; if (oreLevel > levelToMaxOre(bot.getPickaxes().getPickaxeLevel(e.getMember()))) return false; return true; }, e -> { awardDrop(e.getMember(), channel, ore, message -> { cleanup(channel, m, ore); then.accept(channel); message.delete().queueAfter(5, TimeUnit.SECONDS); }); }, DROP_DURATION_SECONDS, TimeUnit.SECONDS, () -> { cleanup(channel, m, ore); }); }); } public void cleanup(MessageChannel channel, Message message, MineshaftItem ore) { message.delete().queue(a -> {}, x -> {}); channel.getHistoryAfter(message, 100).queueAfter(5, TimeUnit.SECONDS, h -> { ArrayList msgs = new ArrayList(); h.getRetrievedHistory().forEach(m -> { if (m.getContentRaw().equalsIgnoreCase(getPickString(ore))) { msgs.add(m); } }); PeelingUtils.bulkDelte(channel, msgs); }); ; } public void sendDrop(MessageChannel channel, MineshaftItem ore, boolean nether, Consumer callback) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setColor(bot.getColor()); embedBuilder.setImage(getOreUrl(ore, nether)); embedBuilder.setTitle(String.format(DROP_TITLE)); embedBuilder.setDescription(String.format(DROP_DESCRIPTION, getPickString(ore), PeelingUtils.formatTimeRelativeFromNow(DROP_DURATION_SECONDS * 1000l))); channel.sendMessageEmbeds(embedBuilder.build()).queue(callback); } public void awardDrop(Member member, MessageChannel channel, MineshaftItem ore, Consumer callback) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setColor(bot.getColor()); Map awards = getAwards(member, ore); bot.addItems(member, awards); String awardsList = bot.createItemList(awards, "+%s"); embedBuilder.setDescription(member.getAsMention() + " mined the ore!\n " + awardsList); channel.sendMessageEmbeds(embedBuilder.build()).queue(callback); } public void endDrop(Message message, MineshaftItem ore) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setColor(bot.color); embedBuilder.setTitle(DROP_END_MESSAGE); embedBuilder.setDescription(DROP_END_MESSAGE2); message.editMessageEmbeds(embedBuilder.build()).queue(m -> { message.delete().queueAfter(1, TimeUnit.MINUTES); }); } public String getOreUrl(MineshaftItem ore, boolean nether) { String prefix = ""; if (nether) prefix = "nether_"; String oreName = prefix + ore.toString(); if (ores.containsKey(oreName)) return ores.get(oreName); if (ores.size() == 0) return ""; return ores.entrySet().stream().findFirst().get().getValue(); } public String getPickString(MineshaftItem ore) { return "mine " + ore.toString(); } public Map getAwards(Member member, MineshaftItem ore) { Map award = new HashMap<>(); switch (ore) { case COAL: award.put(MineshaftItem.COAL, (long) Math.ceil(Math.random() * COAL_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT)); break; case IRON: award.put(MineshaftItem.IRON, (long) Math.ceil(Math.random() * IRON_SPAWN_AMOUNT)); break; case COPPER: award.put(MineshaftItem.COPPER, (long) Math.ceil(Math.random() * COPPER_SPAWN_AMOUNT)); break; case LAPIS: award.put(MineshaftItem.LAPIS, (long) Math.ceil(Math.random() * LAPIS_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 2l)); break; case REDSTONE: award.put(MineshaftItem.REDSTONE, (long) Math.ceil(Math.random() * REDSTONE_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 5l)); break; case GOLD: award.put(MineshaftItem.GOLD, (long) Math.ceil(Math.random() * GOLD_SPAWN_AMOUNT)); break; case EMERALD: award.put(MineshaftItem.EMERALD, (long) Math.ceil(Math.random() * EMERALD_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 5l)); break; case DIAMOND: award.put(MineshaftItem.DIAMOND, (long) Math.ceil(Math.random() * DIAMOND_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 10l)); break; case OBSIDIAN: award.put(MineshaftItem.OBSIDIAN, (long) Math.ceil(Math.random() * OBSIDIAN_SPAWN_AMOUNT)); break; case QUARTZ: award.put(MineshaftItem.QUARTZ, (long) Math.ceil(Math.random() * QUARTZ_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 5l)); break; case SKULL: award.put(MineshaftItem.SKULL, (long) Math.ceil(Math.random() * SKULL_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 10l)); break; case NETHERITE: award.put(MineshaftItem.NETHERITE, (long) Math.ceil(Math.random() * NETHERITE_SPAWN_AMOUNT)); award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 10l)); break; default: break; } return bot.multiply(award, getPickaxeFortuneCurve(member)); } public double getEfficiencyCurve(Member m) { return 1 + Math.pow(0.5, bot.getPickaxes().getPickaxeEfficiency(m) / 2); } private double getPickaxeFortuneCurve(Member m) { int fortune = bot.getPickaxes().getPickaxeFortune(m); return fortune + Math.pow(1.2, fortune + 1); } }