summaryrefslogtreecommitdiff
path: root/src/main/java/net
diff options
context:
space:
mode:
authordavidovski <david@davidovski.xyz>2025-10-11 04:55:51 +0100
committerdavidovski <david@davidovski.xyz>2025-10-11 04:55:51 +0100
commit10b327e9f6441a8863227ce3e075a5a587654065 (patch)
tree5e94ecc91290febacf5181d3596563a7a24bd010 /src/main/java/net
v0.3.1
Diffstat (limited to 'src/main/java/net')
-rw-r--r--src/main/java/net/uomc/mineshaft/DailyCommand.java40
-rw-r--r--src/main/java/net/uomc/mineshaft/EnchantCommand.java132
-rw-r--r--src/main/java/net/uomc/mineshaft/FishCommand.java110
-rw-r--r--src/main/java/net/uomc/mineshaft/FurnaceCommand.java113
-rw-r--r--src/main/java/net/uomc/mineshaft/MineCommand.java280
-rw-r--r--src/main/java/net/uomc/mineshaft/Mineshaft.java272
-rw-r--r--src/main/java/net/uomc/mineshaft/MineshaftItem.java33
-rw-r--r--src/main/java/net/uomc/mineshaft/PickaxeCommand.java112
-rw-r--r--src/main/java/net/uomc/mineshaft/Pickaxes.java168
-rw-r--r--src/main/java/net/uomc/mineshaft/RobCommand.java142
-rw-r--r--src/main/java/net/uomc/mineshaft/RunMineshaft.java46
-rw-r--r--src/main/java/net/uomc/mineshaft/crafting/Crafting.java137
-rw-r--r--src/main/java/net/uomc/mineshaft/crafting/CraftingRecipe.java66
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/Resource.java145
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/ResourceManager.java224
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/ResourceTable.java127
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/CreateResourceCommand.java44
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/GiveResourcesCommand.java85
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/LeaderBoardCommand.java50
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/ListResourcesCommand.java31
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/MyResourcesCommand.java53
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/RemoveResourceCommand.java46
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/ResourceCommand.java17
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/SetPrimaryResourceCommand.java47
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/commands/ValuesCommand.java45
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/market/MarketCommand.java159
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/market/MarketManager.java128
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/market/Offer.java104
28 files changed, 2956 insertions, 0 deletions
diff --git a/src/main/java/net/uomc/mineshaft/DailyCommand.java b/src/main/java/net/uomc/mineshaft/DailyCommand.java
new file mode 100644
index 0000000..fc814d1
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/DailyCommand.java
@@ -0,0 +1,40 @@
+package net.uomc.mineshaft;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+
+public class DailyCommand extends CooldownCommand {
+
+ Mineshaft bot;
+
+ public DailyCommand(Mineshaft bot) {
+ super(bot);
+ setCommandDetails(CommandDetails.from("daily", "get free ores", "furnace"));
+ this.bot = bot;
+
+ setCooldown(24l* 60l * 60l * 1000l);
+ }
+
+ @Override
+ public boolean trigger(MessageReceivedEvent e) {
+
+
+ Map<MineshaftItem, Long> award = new LinkedHashMap<>();
+ for (int i = 0; i < 10; ++i) {
+ award = bot.sumItems(award,
+ bot.getMineCommand().getAwards(e.getMember(),
+ bot.getMineCommand().getOre(
+ bot.getPickaxes().getPickaxeLevel(e.getMember()))));
+ }
+ ;
+
+ String awardsList = bot.createItemList(award, "+%s", ", ");
+ bot.sendSuccessMessage(e, "You received your daily ores:\n**" + awardsList +"**");
+ return true;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/EnchantCommand.java b/src/main/java/net/uomc/mineshaft/EnchantCommand.java
new file mode 100644
index 0000000..f85c92b
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/EnchantCommand.java
@@ -0,0 +1,132 @@
+package net.uomc.mineshaft;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.CommandFail;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+
+import com.mouldycheerio.dbot.util.PeelingUtils;
+import com.vdurmont.emoji.EmojiParser;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+
+public class EnchantCommand extends DetailedCommand {
+
+ private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/Enchanting_Table.gif";
+ private static final double MIN_DISCOUNT = 0.4;
+ private static final double MAX_BOOKSHELVES = 24;
+ private static final String COMMAND_TITLE = "Enchanting Table";
+ Mineshaft bot;
+
+ public EnchantCommand(Mineshaft bot) {
+ setCommandDetails(CommandDetails.from("enchant,enchanting,ench,etable,enchantingtable", "", "enchant"));
+ this.bot = bot;
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot b, String[] args) throws CommandFail {
+
+ long ench_tables = bot.getItem(e.getMember(), MineshaftItem.ENCHANTING_TABLE);
+
+ if (ench_tables < 1) {
+ b.sendErrorMessage(e, ":x: You need to have at least **"
+ + bot.getItem(MineshaftItem.ENCHANTING_TABLE).prettyValue(1)
+ + "** to use this!");
+ return;
+ }
+
+ if (args.length > 0) {
+ enchant(e, args);
+ return;
+ }
+
+ int fortune = bot.getPickaxes().getPickaxeFortune(e.getMember());
+ long bookshelves = bot.getItem(e.getMember(), MineshaftItem.BOOKSHELF);
+ String bookshelfDiscountPercent = "-" + (int) Math.ceil((1 -bookshelfDiscount(bookshelves))*100) + "%";
+ long xpLevel = getXpLevel(fortune, bookshelves);
+ long lapisLevel = getLapisLevel(fortune, bookshelves);
+
+ long xpLevelNormal = getXpLevel(fortune, 0);
+ long lapisLevelNormal = getLapisLevel(fortune, 0);
+
+ String curr = "";
+ if (fortune > 0) {
+ curr = "Your pickaxe has **Fortune " + Pickaxes.getRomanNumber(fortune) + "**\n\n";
+ }
+
+ String description = curr + "Enchant your pickaxe with `" + b.getPrefixManager().getPrefix(e.getGuild()) + "enchant fortune`\n\n";
+
+ description += "**Cost:**\n";
+
+ if (bookshelves > 0)
+ description += "~~" + PeelingUtils.amountToString(lapisLevelNormal) + "~~ ";
+
+ description += "**" + bot.getItem(MineshaftItem.LAPIS).prettyValue(lapisLevel) + "**";
+ description += "\n";
+
+ if (bookshelves > 0)
+ description += "~~" + PeelingUtils.amountToString(xpLevelNormal) + "~~ ";
+
+ description += "**" + bot.getItem(MineshaftItem.XP).prettyValue(xpLevel) + "**";
+
+ if (bookshelves > 0) {
+ description += "\n\nYour ";
+ description += bot.getItem(MineshaftItem.BOOKSHELF).prettyValue(bookshelves);
+ description += " are giving you a **" + bookshelfDiscountPercent + "** discount!";
+ }
+
+ EmbedBuilder em = new EmbedBuilder();
+ em.setTitle(COMMAND_TITLE);
+ em.setDescription(description);
+ em.setThumbnail(COMMAND_IMAGE);
+ em.setColor(PeelingUtils.hex2Rgb("#0a0711"));
+
+ if (bookshelves == 0) {
+ em.setFooter(EmojiParser.parseToUnicode(":bulb:") + "Bookshelves give you a discount on your enchantments");
+ }
+
+ e.getMessage().replyEmbeds(em.build()).queue();
+ }
+
+ private long getLapisLevel(int fortune, long bookshelves) {
+ return (long) (Math.pow(2, fortune + 4) * bookshelfDiscount(bookshelves));
+ }
+
+ private double bookshelfDiscount(long bookshelves) {
+ double b = Math.min(bookshelves, MAX_BOOKSHELVES);
+ return MIN_DISCOUNT + Math.log10( 1.0 + (9.0 * ((MAX_BOOKSHELVES - b) / MAX_BOOKSHELVES))) * MIN_DISCOUNT;
+ }
+
+ private long getXpLevel(int fortune, long bookshelves) {
+ return (long) (15000l * Math.pow(fortune + 1, 2) * bookshelfDiscount(bookshelves));
+ }
+
+ private void enchant(MessageReceivedEvent e, String[] args) {
+ int fortune = bot.getPickaxes().getPickaxeFortune(e.getMember());
+ long bookshelves = bot.getItem(e.getMember(), MineshaftItem.BOOKSHELF);
+ long xpLevel = getXpLevel(fortune, bookshelves);
+ long lapisLevel = getLapisLevel(fortune, bookshelves);
+
+ long memberLapis = bot.getItem(e.getMember(), MineshaftItem.LAPIS);
+ long memberXp = bot.getItem(e.getMember(), MineshaftItem.XP);
+
+ if (memberLapis < lapisLevel) {
+ bot.sendErrorMessage(e, ":x: You need " + bot.getItem(MineshaftItem.LAPIS).prettyValue(lapisLevel - memberLapis) + " more lapis!" );
+ return;
+ }
+
+ if (memberXp < xpLevel) {
+ bot.sendErrorMessage(e, ":x: You need " + bot.getItem(MineshaftItem.XP).prettyValue(xpLevel - memberXp) + " more xp!" );
+ return;
+ }
+
+ bot.removeItem(e.getMember(), MineshaftItem.LAPIS, lapisLevel);
+ bot.removeItem(e.getMember(), MineshaftItem.XP, xpLevel);
+ int level = bot.getPickaxes().incrementFortune(e.getMember());
+
+ bot.sendSuccessMessage(e, ":white_check_mark: You enchanted your pickaxe. Now it has **Fortune " + Pickaxes.getRomanNumber(level) + "**");
+
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/FishCommand.java b/src/main/java/net/uomc/mineshaft/FishCommand.java
new file mode 100644
index 0000000..3df7994
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/FishCommand.java
@@ -0,0 +1,110 @@
+package net.uomc.mineshaft;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+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.vdurmont.emoji.EmojiParser;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.entities.emoji.Emoji;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class FishCommand extends CooldownCommand {
+ private static final double XP_SPAWN_AMOUNT = 2000;
+ private static final double BOOK_SPAWN_CHANCE = 0.05;
+ private static long MIN_WAIT_MS = 1500;
+ private static long MAX_WAIT_MS = 10000;
+
+ private static long MIN_REEL_MS = 1000;
+ private static long MAX_REEL_MS = 4000;
+
+ private static final String REACTION_CLICK = ":fishing_pole_and_fish:";
+ private static final String REACTION_FISH = ":fish:";
+ private static final String TITLE = "Fishing";
+ private static final String IMAGE_ROD = "https://minecraft.wiki/images/Fishing_Rod_JE2_BE2.png";
+
+ private Mineshaft bot;
+
+ protected FishCommand(Mineshaft bot) {
+ super(bot);
+ setCooldown(20l * 1000l);
+ this.bot = bot;
+ setDetails(CommandDetails.from("fish", "go fishing"));
+ }
+
+ @Override
+ public boolean trigger(MessageReceivedEvent e) {
+
+ e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(REACTION_FISH))).queue();
+ double random = Math.random();
+
+ long waitTime = MIN_WAIT_MS + (long) (Math.random() * (MAX_WAIT_MS - MIN_WAIT_MS));
+ long reelTime = MIN_REEL_MS + (long) (Math.random() * (MAX_REEL_MS - MIN_REEL_MS));
+
+ Map<MineshaftItem, Long> awards = getAwards(e.getMember());
+
+ MessageEmbed messageEmbed = bot.createMessageEmbed(e.getMessage(), TITLE, "You sit and wait for a fish...",
+ IMAGE_ROD, Color.GRAY);
+
+ MessageEmbed messageEmbedReel = bot.createMessageEmbed(e.getMessage(), TITLE,
+ "**You feel something bite your bobber!**\n\nHit the reaction to reel in", IMAGE_ROD, bot.getColor());
+
+ MessageEmbed messageEmbedFail = bot.createMessageEmbed(e.getMessage(), TITLE, ":x: The fish managed to get away...",
+ "", Color.GRAY);
+
+ MessageEmbed messageEmbedSuccess = bot.createMessageEmbed(e.getMessage(), TITLE,
+ String.format("CATCH!\n%s", bot.createItemList(awards, "+%s")), "", bot.getColor());
+
+ Consumer<Message> cleanup = m -> {
+ m.clearReactions().queue();
+ m.delete().queueAfter(5, TimeUnit.SECONDS);
+ };
+
+ e.getMessage().replyEmbeds(messageEmbed).queue(message -> {
+ message.addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(REACTION_CLICK)))
+ .queueAfter(waitTime, TimeUnit.MILLISECONDS, r -> {
+ EventWaiter eventWaiter = new EventWaiter();
+ eventWaiter.waitForEvent(MessageReactionAddEvent.class,
+ e1 -> e1.getMessageId().equals(message.getId())
+ && e1.getUser().getId().equals(e.getAuthor().getId()),
+ e1 -> {
+ bot.addItems(e.getMember(), awards);
+ message.editMessageEmbeds(messageEmbedSuccess).queue();
+ }, reelTime, TimeUnit.MILLISECONDS, () -> {
+ message.editMessageEmbeds(messageEmbedFail).queue();
+ ;
+ cleanup.accept(message);
+ });
+ message.editMessageEmbeds(messageEmbedReel).queue(x -> {
+ e.getJDA().addEventListener(eventWaiter);
+ });
+ });
+ });
+
+ return true;
+ }
+
+ public Map<MineshaftItem, Long> getAwards(Member member) {
+ Map<MineshaftItem, Long> award = new HashMap<>();
+ if (Math.random() < BOOK_SPAWN_CHANCE) {
+ award.put(MineshaftItem.BOOK, 1l);
+ } else {
+ award.put(MineshaftItem.FISH, Math.random() > 0.5 ? 1l : 2l);
+ award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT));
+ }
+ return award;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/FurnaceCommand.java b/src/main/java/net/uomc/mineshaft/FurnaceCommand.java
new file mode 100644
index 0000000..8c2b628
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/FurnaceCommand.java
@@ -0,0 +1,113 @@
+package net.uomc.mineshaft;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.CommandFail;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand;
+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.emoji.CustomEmoji;
+import net.dv8tion.jda.api.entities.emoji.Emoji;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+
+public class FurnaceCommand extends CooldownCommand {
+
+ private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/Lit_Furnace_%28S%29.gif?40e71";
+ private static final String COMMAND_TITLE = "Furnace";
+ private static final double COPPER_SPAWN_AMOUNT = 16;
+ private static final double IRON_SPAWN_AMOUNT = 8;
+ private static final double GOLD_SPAWN_AMOUNT = 4;
+ private static final double EMERALD_SPAWN_AMOUNT = 1;
+ private static final double DIAMOND_SPAWN_AMOUNT = 1;
+ private static final double XP_SPAWN_AMOUNT = 1000;
+ private static final long FURNACE_ITEMS = 3;
+ Mineshaft bot;
+
+ public FurnaceCommand(Mineshaft bot) {
+ super(bot);
+ setCommandDetails(CommandDetails.from("furnace", "get free ingots", "furnace"));
+ this.bot = bot;
+ setCooldown(60l * 60l * 1000l);
+ }
+
+ @Override
+ public boolean trigger(MessageReceivedEvent e) {
+
+ long furnaces = bot.getItem(e.getMember(), MineshaftItem.FURNACE);
+
+ if (furnaces < 1) {
+ bot.sendErrorMessage(e, ":x: You need to have at least **"
+ + bot.getItem(MineshaftItem.FURNACE).prettyValue(1)
+ + "** to use this!");
+ return false;
+ }
+
+ Map<MineshaftItem, Long> award = new LinkedHashMap<>();
+ for (int i = 0; i < furnaces * FURNACE_ITEMS; ++i) {
+ award =bot.sumItems(award, getAwards());
+ };
+
+ String description = "You came back to your **" + bot.getItem(MineshaftItem.FURNACE).prettyValue(furnaces) + "** and collected the ingots:\n";
+
+ String awardsList = bot.createItemList(award, "+%s");
+ description += awardsList;
+
+ EmbedBuilder em = new EmbedBuilder();
+ em.setTitle(COMMAND_TITLE);
+ em.setDescription(description);
+ em.setThumbnail(COMMAND_IMAGE);
+ em.setColor(PeelingUtils.hex2Rgb("#5c5a5a"));
+
+ e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(":fire:")));
+ bot.addItems(e.getMember(), award);
+ e.getMessage().replyEmbeds(em.build()).queue();
+ return true;
+ }
+ public Map<MineshaftItem, Long> getAwards() {
+ Map<MineshaftItem, Long> award = new HashMap<>();
+ Random random = new Random();
+ switch (random.nextInt(14)) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ award.put(MineshaftItem.IRON, (long) Math.ceil(Math.random() * IRON_SPAWN_AMOUNT));
+ break;
+ case 4:
+ case 5:
+ case 6:
+ award.put(MineshaftItem.COPPER, (long) Math.ceil(Math.random() * COPPER_SPAWN_AMOUNT));
+ break;
+ case 7:
+ case 8:
+ case 9:
+ award.put(MineshaftItem.GOLD, (long) Math.ceil(Math.random() * GOLD_SPAWN_AMOUNT));
+ break;
+ case 10:
+ case 11:
+ award.put(MineshaftItem.DIAMOND, (long) Math.ceil(Math.random() * DIAMOND_SPAWN_AMOUNT));
+ break;
+ case 12:
+ case 13:
+ default:
+ award.put(MineshaftItem.EMERALD, (long) Math.ceil(Math.random() * EMERALD_SPAWN_AMOUNT));
+ break;
+ }
+
+ award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT));
+
+ return award;
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/MineCommand.java b/src/main/java/net/uomc/mineshaft/MineCommand.java
new file mode 100644
index 0000000..ecee140
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/MineCommand.java
@@ -0,0 +1,280 @@
+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 Map<String, String> ores;
+ private List<String> oresList;
+
+ 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();
+ }
+
+ public List<String> loadOreOrder() {
+ // lololol no
+ return List.of(
+ "coal",
+ "copper",
+ "iron",
+ "lapis",
+ "redstone",
+ "gold",
+ "diamond",
+ "emerald",
+ "obsidian"
+ );
+ }
+
+ public Map<String, String> loadOreImages() {
+ Map<String, String> ores = new LinkedHashMap<String, String>();
+ 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(int pickaxe) {
+ double random = Math.random();
+ int maxOre = levelToMaxOre(pickaxe);
+
+ int i = (int) Math.floor(Math.pow(random, 2) * maxOre);
+ String oreName = oresList.get(i);
+ MineshaftItem ore = MineshaftItem.valueOf(oreName.toUpperCase());
+ return ore;
+ }
+
+ @Override
+ public boolean trigger(MessageReceivedEvent e) {
+
+ int pickaxe = bot.getPickaxes().getPickaxeLevel(e.getMember());
+ MineshaftItem ore = getOre(pickaxe);
+ int i = oresList.indexOf(ore.toString().toLowerCase());
+
+ spawnDrop(e.getChannel(), ore, i, m -> {});
+ e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(":pick:"))).queue();
+ return true;
+ }
+
+ 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
+ // TODO for obsidian u can make nether portal
+ }
+ return 9;
+ }
+
+ public void spawnDrop(MessageChannel channel, MineshaftItem ore, int oreLevel, Consumer<MessageChannel> then) {
+ sendDrop(channel, ore, 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);
+ endDrop(m, ore);
+ });
+
+ });
+ }
+
+ public void cleanup(MessageChannel channel, Message message, MineshaftItem ore) {
+ message.delete().queue();
+
+ channel.getHistoryAfter(message, 100).queueAfter(5, TimeUnit.SECONDS, h -> {
+ ArrayList<Message> msgs = new ArrayList<Message>();
+ h.getRetrievedHistory().forEach(m -> {
+ if (m.getContentRaw().equalsIgnoreCase(getPickString(ore))) {
+ msgs.add(m);
+ }
+ });
+ PeelingUtils.bulkDelte(channel, msgs);
+ });
+ ;
+ }
+
+ public void sendDrop(MessageChannel channel, MineshaftItem ore, Consumer<Message> callback) {
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+
+ embedBuilder.setColor(bot.getColor());
+ embedBuilder.setImage(getOreUrl(ore));
+
+ 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<Message> callback) {
+
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(bot.getColor());
+
+ Map<MineshaftItem, Long> awards = getAwards(member, ore);
+ int fortune = bot.getPickaxes().getPickaxeFortune(member);
+ if (fortune > 0) {
+ bot.multiply(awards, fortune + 1);
+ }
+
+ 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) {
+ String oreName = 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<MineshaftItem, Long> getAwards(Member member, MineshaftItem ore) {
+ Map<MineshaftItem, Long> 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));
+ 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));
+ award.put(MineshaftItem.XP, (long) Math.ceil(Math.random() * XP_SPAWN_AMOUNT * 10l));
+ break;
+ default:
+ break;
+ }
+
+ return award;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/Mineshaft.java b/src/main/java/net/uomc/mineshaft/Mineshaft.java
new file mode 100644
index 0000000..ebdcf21
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/Mineshaft.java
@@ -0,0 +1,272 @@
+package net.uomc.mineshaft;
+
+import net.uomc.mineshaft.crafting.Crafting;
+import net.uomc.mineshaft.resources.Resource;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.collections4.map.LinkedMap;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.AvatarCommand;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+import com.mouldycheerio.dbot.commands.ProfileCommand;
+import com.mouldycheerio.dbot.commands.ServerInfoCommand;
+import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand;
+import com.mouldycheerio.dbot.logging.FullLogger;
+import com.mouldycheerio.dbot.starboard.StarboardController;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class Mineshaft extends CustomBot {
+
+ private StarboardController starboardController;
+ private FullLogger logger;
+ private ResourceManager resourceManager;
+ private Pickaxes pickaxes;
+ private Crafting crafting;
+ private MineCommand mineCommand;
+
+ public Mineshaft(JSONObject config) throws LoginException, JSONException, IOException, InterruptedException {
+ super(config);
+
+ pickaxes = new Pickaxes(this);
+ crafting = new Crafting(this);
+ getCommandController().addCommand(crafting);
+
+ getCommandController().addCommand(new ProfileCommand());
+ getCommandController().addCommand(new AvatarCommand());
+ getCommandController().addCommand(new ServerInfoCommand());
+
+ starboardController = new StarboardController(this);
+
+ resourceManager = new ResourceManager(this);
+
+ getCommandController().addCommand(new DailyCommand(this));
+ getCommandController().addCommand(new EnchantCommand(this));
+ getCommandController().addCommand(new FurnaceCommand(this));
+ getCommandController().addCommand(new PickaxeCommand(this));
+
+ mineCommand = new MineCommand(this);
+ getCommandController().addCommand(mineCommand);
+ getCommandController().addCommand(new FishCommand(this));
+ getCommandController().addCommand(new RobCommand(this));
+
+
+ getCommandController().removeCommand("help");
+ getCommandController().addCommand(CommandDetails.from("help"), (e, b, args) -> {
+ String title = "Mineshaft";
+ StringBuilder notes = new StringBuilder();
+ notes.append("It's time to go mining!\n\n");
+ notes.append("**Mine** ores with `m!mine`, or **fish** with `m!fish`\n");
+ notes.append("Upgrade your **pickaxe** and find new ores `m!pickaxe`\n");
+ notes.append("**Craft** new items `m!craft`\n");
+ notes.append("Fight other players! `m!pvp`\n");
+ notes.append("Share your resources `m!give`\n");
+ notes.append("Become the **top** valuable player `m!top`\n");
+ notes.append("\nGet free **daily** ores with `m!daily`\n");
+
+ sendMessage(e, title, notes.toString());
+ });
+
+ getCommandController().addCommand(CommandDetails.from("patchnotes,changelog"), (e, b, args) -> {
+ // TODO update when changes
+ String title = "Patch Notes";
+ StringBuilder notes = new StringBuilder();
+ notes.append("### v0.3.1\n");
+ notes.append("- Added furnace crafting recipe\n");
+ notes.append("- Added `m!daily` to get daily commands\n");
+ notes.append("- Balanced amount of ores needed to upgrade pickaxe\n");
+ notes.append("- Fix obsidian not spawning\n");
+ notes.append("- Added `m!cooldowns`\n");
+ notes.append("### v0.3\n");
+ notes.append("- Added items for all ores `m!inventory`\n");
+ notes.append("- Added pickaxe levels `m!pickaxe`\n");
+ notes.append(" - Pickaxe can be upgraded, giving access to better ores (`m!pickaxe upgrade`)\n");
+ notes.append("- Added enchanting `m!enchant`\n");
+ notes.append(" - Your pickaxe can be enchanted by spending lapis and xp\n");
+ notes.append(" - Owning bookshelves gives you a discount on the amount of levels it costs to enchant\n");
+ notes.append("- Added crafting `m!craft`\n");
+ notes.append(" - Enchanting table and books can now be crafted!\n");
+ notes.append("- Fishing now drops books\n");
+ notes.append("- Rob has been relaced with `m!kill`\n");
+ notes.append(" - Killing a player will drop some of their items\n");
+ notes.append("- Added obsidian\n");
+ notes.append("- Removed `m!flip`, this will be added in a future update (with a new mechanic)\n");
+
+ sendMessage(e, title, notes.toString());
+ });
+
+ getCommandController().addCommand(CommandDetails.from("cooldowns,cd"), (e, b, args) -> {
+ StringBuilder cooldowns = new StringBuilder();
+ b.getCommandController().getCommands().entrySet().stream().map((x) -> x.getValue())
+ .filter(c -> c instanceof CooldownCommand)
+ .map(c -> (CooldownCommand) c)
+ .sorted((c1, c2) -> {
+ return Long.compareUnsigned(c1.getCooldownController().getNextTime(e.getMember(), c1.getName()),
+ c2.getCooldownController().getNextTime(e.getMember(), c2.getName()));
+ })
+ .forEach(c -> {
+ long nextCooldownTime = ((CooldownCommand) c).getCooldownController()
+ .getNextTime(e.getMember(), ((CooldownCommand) c).getName());
+ if (nextCooldownTime == 0)
+ return;
+ String timeLeft = PeelingUtils.formatTimeRelative(nextCooldownTime);
+
+ if (System.currentTimeMillis() > nextCooldownTime) {
+ return;
+ }
+
+ cooldowns.append("**");
+ cooldowns.append(((CooldownCommand) c).getName());
+ cooldowns.append("**\t");
+ cooldowns.append(timeLeft);
+ cooldowns.append("\n");
+ });
+ if (cooldowns.length() == 0) {
+ cooldowns.append("You have no active cooldowns");
+ }
+
+ b.sendMessage(e, "Active cooldowns", cooldowns.toString());
+ });
+
+ logger = new FullLogger(this);
+ }
+
+ public StarboardController getStarboardController() {
+ return starboardController;
+ }
+
+ public FullLogger getLogger() {
+ return logger;
+ }
+
+ public ResourceManager getResourceManager() {
+ return resourceManager;
+ }
+
+ public long getItem(Member member, MineshaftItem item) {
+ return resourceManager.getResource(member, item.toString());
+ }
+
+ public Resource getItem(MineshaftItem item) {
+ return resourceManager.getResource(item.toString());
+ }
+
+ public void setItem(Member member, MineshaftItem item, long amount) {
+ resourceManager.setResource(member, item.toString(), amount);
+ }
+
+ public void addItem(Member member, MineshaftItem item, long amount){
+ resourceManager.addResource(member, item.toString(), amount);
+ }
+
+ public void addItems(Member member, Map<MineshaftItem, Long> items) {
+ resourceManager.addResources(member, items.entrySet().stream()
+ .collect(Collectors.toMap(
+ e -> e.getKey().toString(),
+ Map.Entry::getValue
+ ))
+ );
+ }
+
+ public Map<MineshaftItem, Long> sumItems(Map<MineshaftItem, Long> items1, Map<MineshaftItem, Long> items2) {
+ Map<MineshaftItem, Long> items = new HashMap<>(items1);
+ items2.forEach((k, v) -> {
+ if (items.containsKey(k)) {
+ items.put(k, items.get(k) + v);
+ } else {
+ items.put(k, v);
+ }
+ });
+ return items;
+ }
+
+ public void removeItem(Member member, MineshaftItem item, long amount) {
+ resourceManager.addResource(member, getItem(item).getName(), -amount);
+ }
+
+ public void removeItems(Member member, Map<MineshaftItem, Long> items) {
+ addItems(member, items.entrySet().stream().collect(Collectors.toMap(
+ Map.Entry::getKey,
+ e -> -e.getValue()
+ )));
+ }
+
+ public String createItemList(Map<MineshaftItem, Long> items) {
+ LinkedHashMap<MineshaftItem, Long> itemsLinked = new LinkedHashMap<>(items);
+ return resourceManager.createResourceList(itemsLinked.entrySet().stream()
+ .collect(Collectors.toMap(
+ e -> e.getKey().toString(),
+ Map.Entry::getValue
+ ))
+ );
+ }
+
+ public String createItemList(Map<MineshaftItem, Long> items, String format) {
+ return Arrays.asList(
+ createItemList(items).split("\n")).stream()
+ .map(s -> String.format(format, s)).collect(Collectors.joining("\n"));
+ }
+
+ public String createItemList(Map<MineshaftItem, Long> items, String format, String join) {
+ return Arrays.asList(
+ createItemList(items).split("\n")).stream()
+ .map(s -> String.format(format, s)).collect(Collectors.joining(join));
+ }
+
+ public Map<MineshaftItem, Long> multiply(Map<MineshaftItem, Long> items, double amount) {
+ return items.entrySet().stream().collect(Collectors.toMap(
+ e -> e.getKey(),
+ e -> (long) Math.floor(e.getValue() * amount)
+ ));
+ }
+
+ public Map<MineshaftItem, Long> getInventory(Member member) {
+ Map<MineshaftItem, Long> inv = new HashMap<>();
+ resourceManager.getResources().forEach(r -> {
+ MineshaftItem item = MineshaftItem.valueOf(r.getName().toUpperCase());
+ inv.put(item, r.get(member));
+ });
+
+ return inv;
+ }
+
+ public boolean hasItems(Member member, Map<MineshaftItem, Long> items) {
+ return items.entrySet().stream().map(e -> {
+ long a = getItem(member, e.getKey());
+ return a >= e.getValue();
+ }).reduce(Boolean.TRUE, Boolean::logicalAnd);
+ }
+
+ public Pickaxes getPickaxes() {
+ return pickaxes;
+ }
+
+ public MineCommand getMineCommand() {
+ return mineCommand;
+ }
+
+ public Map<MineshaftItem, Long> getMissingIngredients(Member member, Map<MineshaftItem, Long> items) {
+ Map<MineshaftItem, Long> lacking = new HashMap<MineshaftItem, Long>();
+ items.forEach((item, q) -> {
+ if (getItem(member, item) < q) {
+ lacking.put(item, q - getItem(member, item));
+ }
+ });
+
+ return Collections.unmodifiableMap(lacking);
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/MineshaftItem.java b/src/main/java/net/uomc/mineshaft/MineshaftItem.java
new file mode 100644
index 0000000..91a4957
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/MineshaftItem.java
@@ -0,0 +1,33 @@
+package net.uomc.mineshaft;
+
+public enum MineshaftItem {
+ COAL("coal"),
+ IRON("iron"),
+ COPPER("copper"),
+ LAPIS("lapis"),
+ REDSTONE("redstone"),
+ GOLD("gold"),
+ EMERALD("emerald"),
+ DIAMOND("diamond"),
+ NETHERITE("netherite"),
+ OBSIDIAN("obsidian"),
+ FISH("fish"),
+ BOOK("book"),
+ XP("xp"),
+ ENCHANTING_TABLE("enchanting_table"),
+ BOOKSHELF("bookshelf"),
+ FURNACE("furnace")
+ ;
+
+ private final String text;
+
+ MineshaftItem(String string) {
+ text = string;
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/PickaxeCommand.java b/src/main/java/net/uomc/mineshaft/PickaxeCommand.java
new file mode 100644
index 0000000..6ca3e08
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/PickaxeCommand.java
@@ -0,0 +1,112 @@
+package net.uomc.mineshaft;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.CommandFail;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+
+public class PickaxeCommand extends DetailedCommand {
+
+ private Mineshaft bot;
+
+ public PickaxeCommand(Mineshaft b) {
+ setCommandDetails(CommandDetails.from("pickaxe,pick", "", "pickaxe [user]"));
+ this.bot = b;
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot b, String[] args) throws CommandFail {
+ if (args.length > 0 && "upgrade".equalsIgnoreCase(args[0])) {
+ int level = bot.getPickaxes().getPickaxeLevel(e.getMember());
+ Map<MineshaftItem, Long> upgradeCost = upgradeCost(e.getMember());
+
+ if (level >= bot.getPickaxes().getMaPickaxeLevel()) {
+ bot.sendErrorMessage(e, ":x: Your pickaxe is already at the maximum level");
+ return;
+ }
+
+ if (!bot.hasItems(e.getMember(), upgradeCost)) {
+ bot.sendErrorMessage(e,
+ ":x: You need **" + String.join(",", bot.createItemList(bot.getMissingIngredients(e.getMember(), upgradeCost)).split("\n")) + "** to upgrade!"
+ );
+ return;
+ }
+
+ bot.removeItems(e.getMember(), upgradeCost);
+ bot.getPickaxes().setLevel(e.getMember(), level + 1);
+ String pickName = bot.getPickaxes().getPickaxeName(e.getMember());
+ bot.sendSuccessMessage(e, "Your pickaxe has been upgraded to a **" + pickName + " Pickaxe**");
+ return;
+ }
+
+ String pickName = bot.getPickaxes().getPickaxeName(e.getMember());
+ String nextPick = bot.getPickaxes().getNextPickaxeName(e.getMember());
+ String pickImage = bot.getPickaxes().getPickaxeImage(e.getMember());
+ int fortune = bot.getPickaxes().getPickaxeFortune(e.getMember());
+
+ Member target = PeelingUtils.getSingleMentionFromArgs(e);
+ String p1 = target.equals(e.getMember()) ? "You currently have" : String.format("%s currently has", e.getMember().getEffectiveName());
+ String p2 = target.equals(e.getMember()) ? "Your pickaxe" : String.format("%s's pickaxe", e.getMember().getEffectiveName());
+
+
+ Map<MineshaftItem, Long> upgradeCost = upgradeCost(target);
+
+ String upgradeString = "\n\nTo upgrade to a **" + nextPick + " Pickaxe **, use `" + bot.getCommandController().getPrefix() + "pickaxe upgrade`.\nUpgrade cost: **" + String.join(",", bot.createItemList(upgradeCost).split("\n")) + "**";
+ if (!target.equals(e.getMember())){
+ upgradeString = "";
+ }
+
+ String enchants = String.format("\n*%s pickaxe has no enchantments*", p2);
+ if (fortune > 0) {
+ enchants = String.format("\n%s pickaxe is enchanted with **Fortune %s**", p2,
+ Pickaxes.getRomanNumber(fortune));
+ }
+
+ EmbedBuilder em = new EmbedBuilder();
+ em.setTitle(p2);
+ em.setDescription(p1 + " a **" + pickName + " Pickaxe**" + enchants + upgradeString);
+ em.setThumbnail(pickImage);
+
+ e.getMessage().replyEmbeds(em.build()).queue();
+ }
+
+ private Map<MineshaftItem, Long> upgradeCost(Member m) {
+ Map<MineshaftItem, Long> cost = new HashMap<>();
+
+ int level = bot.getPickaxes().getPickaxeLevel(m);
+
+ switch ((int) level) {
+ case 0:
+ cost.put(MineshaftItem.COPPER, 16l);
+ //cost.put(MineshaftItem.XP, 5000l);
+ break;
+
+ case 1:
+ cost.put(MineshaftItem.IRON, 32l);
+ //cost.put(MineshaftItem.XP, 10000l);
+ break;
+ case 2:
+ cost.put(MineshaftItem.GOLD, 48l);
+ //cost.put(MineshaftItem.XP, 64000l);
+ break;
+ case 3:
+ cost.put(MineshaftItem.DIAMOND, 64l);
+ //cost.put(MineshaftItem.XP, 128000l);
+ break;
+ case 4:
+ cost.put(MineshaftItem.DIAMOND, 9999999999999999l);
+ break;
+ }
+
+ return cost;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/Pickaxes.java b/src/main/java/net/uomc/mineshaft/Pickaxes.java
new file mode 100644
index 0000000..42a1f92
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/Pickaxes.java
@@ -0,0 +1,168 @@
+package net.uomc.mineshaft;
+
+import java.io.File;
+import java.sql.SQLException;
+import java.util.Collections;
+
+import org.json.JSONObject;
+
+import com.mouldycheerio.dbot.util.DatabaseUtils;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Member;
+
+class Pickaxes {
+
+ private static final int MAX_PICKAXE_LEVEL = 5;
+
+ private static final String LEVEL_TABLE = "levels";
+ private static final String FORTUNE_TABLE = "ench_fortune";
+
+ private String pickaxesDb;
+
+ public Pickaxes(Mineshaft mineshaft) {
+ pickaxesDb = (new File(mineshaft.getDatadir(), "pickaxes.db")).getPath();
+ initDB();
+ }
+
+ private void initDB() {
+ try {
+ DatabaseUtils.createSimpleKVtable(pickaxesDb, LEVEL_TABLE);
+ DatabaseUtils.createSimpleKVtable(pickaxesDb, FORTUNE_TABLE);
+ } catch (SQLException e) {
+ // e.printStackTrace();
+ }
+ }
+
+ public void setLevel(Member member, long value) {
+ try {
+ DatabaseUtils.putInKVtable(pickaxesDb, LEVEL_TABLE, member.getId() + ":" + member.getGuild().getId(), value);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public long getLevel(Member member) {
+ try {
+ return DatabaseUtils.getInKVtable(pickaxesDb, LEVEL_TABLE, member.getId() + ":" + member.getGuild().getId());
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0l;
+ }
+
+ private void setFortune(Member member, long value) {
+ try {
+ DatabaseUtils.putInKVtable(pickaxesDb, FORTUNE_TABLE, member.getId() + ":" + member.getGuild().getId(), value);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private long getFortune(Member member) {
+ try {
+ return DatabaseUtils.getInKVtable(pickaxesDb, FORTUNE_TABLE, member.getId() + ":" + member.getGuild().getId());
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0l;
+ }
+
+ public boolean isEnchanted(Member member) {
+ long fortune = getFortune(member);
+ return fortune > 0;
+ }
+
+ public String getPickaxeName(Member member) {
+ long level = getLevel(member);
+ return pickaxeLevelToName(level);
+ }
+
+ public String getNextPickaxeName(Member member) {
+ long level = getLevel(member) + 1;
+ if (level == getMaPickaxeLevel())
+ level = getMaPickaxeLevel();
+ return pickaxeLevelToName(level);
+ }
+
+ private static String pickaxeLevelToName(long level) {
+ switch ((int) level) {
+ case 0:
+ return "Stone";
+ case 1:
+ return "Copper";
+ case 2:
+ return "Iron";
+ case 3:
+ return "Gold";
+ case 4:
+ return "Diamond";
+ default:
+ return "Netherite";
+ }
+ }
+
+ public String getPickaxeImage(Member member) {
+ long level = getLevel(member);
+ switch ((int) level) {
+ case 0:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Enchanted_Stone_Pickaxe.gif"
+ : "https://minecraft.wiki/images/Stone_Pickaxe_JE2_BE2.png";
+ case 1:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Enchanted_Copper_Pickaxe.webp"
+ : "https://minecraft.wiki/images/Copper_Pickaxe_JE1_BE1.png";
+ case 2:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Enchanted_Iron_Pickaxe.gif"
+ : "https://minecraft.wiki/images/Iron_Pickaxe_JE3_BE2.png";
+ case 3:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Enchanted_Golden_Pickaxe.gif"
+ : "https://minecraft.wiki/images/Golden_Pickaxe_JE4_BE3.png";
+ case 4:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Enchanted_Diamond_Pickaxe.gif"
+ : "https://minecraft.wiki/images/Diamond_Pickaxe_JE3_BE3.png";
+ default:
+ return isEnchanted(member) ?
+ "https://minecraft.wiki/images/Netherite_Pickaxe_JE3.png"
+ : "https://minecraft.wiki/images/Enchanted_Netherite_Pickaxe.gif";
+ }
+ }
+
+ public int getMaPickaxeLevel() {
+ return MAX_PICKAXE_LEVEL;
+ }
+
+ public int getPickaxeLevel(Member member) {
+ return (int) getLevel(member);
+ }
+
+ public int getPickaxeFortune(Member member) {
+ return (int) getFortune(member);
+ }
+
+ public int incrementFortune(Member member) {
+ setFortune(member, getFortune(member) + 1);
+ return (int) getFortune(member);
+ }
+
+ public static String getRomanNumber(int number) {
+ return String.join("", Collections.nCopies(number, "I"))
+ .replace("IIIII", "V")
+ .replace("IIII", "IV")
+ .replace("VV", "X")
+ .replace("VIV", "IX")
+ .replace("XXXXX", "L")
+ .replace("XXXX", "XL")
+ .replace("LL", "C")
+ .replace("LXL", "XC")
+ .replace("CCCCC", "D")
+ .replace("CCCC", "CD")
+ .replace("DD", "M")
+ .replace("DCD", "CM");
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/RobCommand.java b/src/main/java/net/uomc/mineshaft/RobCommand.java
new file mode 100644
index 0000000..c04f2f4
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/RobCommand.java
@@ -0,0 +1,142 @@
+package net.uomc.mineshaft;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+import com.mouldycheerio.dbot.util.EventWaiter;
+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.MessageEmbed;
+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;
+
+public class RobCommand extends CooldownCommand {
+
+ private static final int TO_PICK = 3;
+ private static final double MAX_AMOUNT_PERCENT = 0.2;
+ private static final long DROP_DURATION_SECONDS = 90;
+ private static final String LOOT_STRING = "loot";
+ private Mineshaft bot;
+ private @NotNull String embedTitle;
+
+ protected RobCommand(Mineshaft bot) {
+ super(bot);
+ setCooldown(60l * 30l * 1000l);
+ this.bot = bot;
+ setDetails(CommandDetails.from("kill,pvp,rob", "kill another player"));
+ embedTitle = EmojiParser.parseToUnicode(":crossed_swords:");
+ }
+
+ @Override
+ public boolean trigger(MessageReceivedEvent e) {
+ String[] args = e.getMessage().getContentRaw().split(" ");
+ if (args.length < 2) {
+ e.getMessage().reply(":x: Please specify a target! `kill @user`").queue();
+ return false;
+ }
+
+ Optional<Member> mentionToMember = PeelingUtils.mentionToMember(args[1], e.getGuild());
+ if (!mentionToMember.isPresent()) {
+ e.getMessage().reply(":x: Invalid Target! `kill @user`").queue();
+ return false;
+ }
+ Member target = mentionToMember.get();
+
+ String response = target.getAsMention() + " was slain by " + e.getAuthor().getAsMention();
+
+ Map<MineshaftItem, Long> rob = getRob(target);
+
+ EmbedBuilder eb = new EmbedBuilder();
+ eb.setTitle(embedTitle);
+ eb.getDescriptionBuilder().append(response);
+ eb.getDescriptionBuilder().append("\nThey dropped:\n\n");
+ eb.getDescriptionBuilder().append(bot.createItemList(rob));
+ eb.getDescriptionBuilder().append("\n\nSay `" + LOOT_STRING + "` to loot these items.\nHurry you only have " +PeelingUtils.formatTimeRelativeFromNow(DROP_DURATION_SECONDS * 1000l) + " to collect them!");
+ eb.setColor(bot.color);
+
+ e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(":crossed_swords:"))).queue();
+ spawnDrop(e, eb.build(), target, rob);
+
+ return true;
+ }
+
+ public Map<MineshaftItem, Long> getRob(Member member) {
+ Map<MineshaftItem, Long> rob = new HashMap<>();
+
+ Map<MineshaftItem, Long> inventory = bot.getInventory(member);
+
+ int items = TO_PICK;
+ if (bot.getItem(member, MineshaftItem.XP) > 0) {
+ rob.put(MineshaftItem.XP,
+ (long) Math.ceil(bot.getItem(member, MineshaftItem.XP) * (Math.random() * MAX_AMOUNT_PERCENT))
+ );
+ items--;
+ }
+
+ int random = new Random().nextInt();
+ inventory.entrySet().stream().filter(e-> e.getValue() > 0 && e.getKey() != MineshaftItem.XP)
+ .sorted(Comparator.comparingInt(o -> System.identityHashCode(o) ^ random))
+ .limit(items).collect(Collectors.toList()).forEach(e -> {
+ rob.put(e.getKey(),
+ (long) Math.ceil(e.getValue() * (Math.random() * MAX_AMOUNT_PERCENT))
+ );
+ });
+
+
+ return rob;
+ }
+
+ public void spawnDrop(MessageReceivedEvent event, MessageEmbed embed, Member target, Map<MineshaftItem, Long> rob) {
+ bot.removeItems(target, rob);
+
+ event.getChannel().sendMessageEmbeds(embed).queue( m -> {
+
+ EventWaiter eventWaiter = new EventWaiter();
+ event.getChannel().getJDA().addEventListener(eventWaiter);
+
+ eventWaiter.waitForEvent(MessageReceivedEvent.class,
+ e -> {
+ if (!e.getMessage().getContentRaw().equalsIgnoreCase(LOOT_STRING))
+ return false;
+
+ return true;
+ }, e -> {
+ bot.addItems(e.getMember(), rob);
+ bot.editMessage(m, embedTitle, e.getMember().getAsMention() + " claimed the loot!\n**" + bot.createItemList(rob, "+%s") + "**");
+ cleanup(event.getChannel(), m);
+ }, DROP_DURATION_SECONDS, TimeUnit.SECONDS, () -> {
+ m.delete().queue();
+ cleanup(event.getChannel(), m);
+ });
+ });
+ }
+ public void cleanup(MessageChannel channel, Message message) {
+ message.delete().queueAfter(60, TimeUnit.SECONDS);
+
+ channel.getHistoryAfter(message, 100).queueAfter(1, TimeUnit.SECONDS, h -> {
+ ArrayList<Message> msgs = new ArrayList<Message>();
+ h.getRetrievedHistory().forEach(m -> {
+ if (m.getContentRaw().equalsIgnoreCase(LOOT_STRING)) {
+ msgs.add(m);
+ }
+ });
+ PeelingUtils.bulkDelte(channel, msgs);
+ });
+ ;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/RunMineshaft.java b/src/main/java/net/uomc/mineshaft/RunMineshaft.java
new file mode 100644
index 0000000..9115561
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/RunMineshaft.java
@@ -0,0 +1,46 @@
+package net.uomc.mineshaft;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.time.LocalDateTime;
+
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+public class RunMineshaft {
+ private static JSONObject config;
+
+ public static void main(String[] args) throws InterruptedException {
+ LocalDateTime time = LocalDateTime.now();
+ System.out.println("");
+ System.out.println("===================================== " + time.getDayOfMonth() + "/" + time.getMonthValue() + "/" + time.getYear() + " " + time.getHour() + ":" + time.getMinute() + ":" + time.getSecond() + " =====================================");
+ config = new JSONObject();
+ load();
+
+ try {
+ new Mineshaft(config);
+
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ System.exit(0);
+
+ }
+
+ }
+
+ public static void load() {
+
+ try {
+ JSONTokener parser = new JSONTokener(new FileReader("config.json"));
+ JSONObject obj = (JSONObject) parser.nextValue();
+ config = obj;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/crafting/Crafting.java b/src/main/java/net/uomc/mineshaft/crafting/Crafting.java
new file mode 100644
index 0000000..5bbef1c
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/crafting/Crafting.java
@@ -0,0 +1,137 @@
+package net.uomc.mineshaft.crafting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+import com.vdurmont.emoji.EmojiParser;
+import com.mouldycheerio.dbot.commands.CommandFail;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.Mineshaft;
+import net.uomc.mineshaft.MineshaftItem;
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+
+public class Crafting extends DetailedCommand {
+ private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/Crafting_Table_JE4_BE3.png";
+ private static final String COMMAND_TITLE = "Crafting Table";
+ List<CraftingRecipe> recipes;
+ private Mineshaft bot;
+
+ public Crafting(Mineshaft bot) {
+ setDetails(CommandDetails.from("craft", "Craft an item", "craft [item]"));
+ this.bot = bot;
+
+ recipes = new ArrayList<>();
+
+ addRecipe(
+ CraftingRecipe.make(MineshaftItem.ENCHANTING_TABLE)
+ .addIngredient(MineshaftItem.BOOK, 1)
+ .addIngredient(MineshaftItem.OBSIDIAN, 4)
+ .addIngredient(MineshaftItem.DIAMOND, 2)
+ .setBuyMessage("Use `" + bot.getCommandController().getPrefix() + "enchant` to use your enchanting table!")
+ );
+
+ addRecipe(
+ CraftingRecipe.make(MineshaftItem.BOOKSHELF)
+ .addIngredient(MineshaftItem.BOOK, 3)
+ .setBuyMessage("**+1 enchanting table level**")
+ );
+
+ addRecipe(
+ CraftingRecipe.make(MineshaftItem.FURNACE)
+ .addIngredient(MineshaftItem.COAL, 800l)
+ .setBuyMessage("Use `" + bot.getCommandController().getPrefix() + "furnace` to get free ores")
+ );
+ }
+
+
+ public List<CraftingRecipe> getRecipes() {
+ return recipes;
+ }
+
+ public Crafting addRecipe(CraftingRecipe craft) {
+ recipes.add(craft);
+ return this;
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot b, String[] args) throws CommandFail {
+ if (args.length > 0) {
+ craft(e, args);
+ return;
+ }
+
+ String description = "This is your crafting table. Craft an item with `" + b.getPrefixManager().getPrefix(e.getGuild()) + "craft [item]`\n\n";
+
+ description += getCraftingRecipesList();
+
+ EmbedBuilder em = new EmbedBuilder();
+ em.setTitle(COMMAND_TITLE);
+ em.setDescription(description);
+ em.setThumbnail(COMMAND_IMAGE);
+ em.setColor(PeelingUtils.hex2Rgb("#93764c"));
+
+ em.setFooter(EmojiParser.parseToUnicode(":bulb:") + "View your inventory with " +b.getPrefixManager().getPrefix(e.getGuild()) + "inv");
+
+ e.getMessage().replyEmbeds(em.build()).queue();
+ }
+
+ public String getCraftingRecipesList() {
+ return getRecipes().stream().map(recipe -> {
+ Resource r = bot.getItem(recipe.getItem());
+
+ return "**" + r.getPrettyName() + "**\n"
+ + bot.createItemList(recipe.getIngredients(), "%s", ", ")
+ + " -> "
+ + r.prettyValue(recipe.getQuantity());
+ }).collect(Collectors.joining("\n\n"));
+ }
+
+
+ private void craft(MessageReceivedEvent e, String[] args) {
+ long amount = 1;
+ String toCraft;
+ if (PeelingUtils.isLong(args[args.length-1])) {
+ amount = Long.parseLong(args[args.length-1]);
+ toCraft = String.join("_", Arrays.copyOfRange(args, 0, args.length -1)).toLowerCase();
+ } else {
+ toCraft = String.join("_", args).toLowerCase();
+ }
+
+ Optional<CraftingRecipe> selected = getRecipes().stream().filter(r -> r.getItem().toString().equalsIgnoreCase(toCraft)).findFirst();
+
+ if (selected.isEmpty()) {
+ bot.sendErrorMessage(e, ":x: That item does not have a crafting recipe");
+ return;
+ }
+
+ CraftingRecipe recipe = selected.get();
+ Map<MineshaftItem, Long> missing = recipe.getMissingIngredients(bot.getInventory(e.getMember()), amount);
+
+ if (!missing.isEmpty()) {
+ bot.sendErrorMessage(e, ":x: Unable to craft!\nIngredients missing: **" + bot.createItemList(missing, "%s", ", ") + "**");
+ return;
+ }
+
+ bot.removeItems(e.getMember(), bot.multiply(recipe.getIngredients(), amount));
+ bot.addItem(e.getMember(), recipe.getItem(), recipe.getQuantity() * amount);
+ bot.sendSuccessMessage(e,
+ "You crafted " + bot.getItem(recipe.getItem()).prettyValue(recipe.getQuantity() * amount)
+ + "!\n" + recipe.getBuyMessage()
+ );
+
+ }
+
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/crafting/CraftingRecipe.java b/src/main/java/net/uomc/mineshaft/crafting/CraftingRecipe.java
new file mode 100644
index 0000000..cb4892e
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/crafting/CraftingRecipe.java
@@ -0,0 +1,66 @@
+package net.uomc.mineshaft.crafting;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import net.uomc.mineshaft.MineshaftItem;
+
+public class CraftingRecipe {
+ MineshaftItem item;
+ long quantity;
+ Map<MineshaftItem, Long> ingredients;
+
+ String buyMessage;
+
+ private CraftingRecipe(MineshaftItem item, long quantity, Map<MineshaftItem, Long> ingredients) {
+ this.item = item;
+ this.quantity = quantity;
+ this.ingredients = ingredients;
+ }
+
+ public static CraftingRecipe make(MineshaftItem item) {
+ Map<MineshaftItem, Long> ingredients = new HashMap<>();
+ return new CraftingRecipe(item, 1, ingredients);
+ }
+
+ public CraftingRecipe addIngredient(MineshaftItem item, long amount) {
+ ingredients.put(item, amount);
+ return this;
+ }
+
+ public MineshaftItem getItem() {
+ return item;
+ }
+
+ public long getQuantity() {
+ return quantity;
+ }
+
+ public Map<MineshaftItem, Long> getIngredients() {
+ return ingredients;
+ }
+
+ public String getBuyMessage() {
+ return buyMessage;
+ }
+
+ public CraftingRecipe setBuyMessage(String message) {
+ buyMessage = message;
+ return this;
+ }
+ public Map<MineshaftItem, Long> getMissingIngredients(Map<MineshaftItem, Long> items) {
+ return getMissingIngredients(items, 1);
+ }
+
+ public Map<MineshaftItem, Long> getMissingIngredients(Map<MineshaftItem, Long> items, long amount) {
+ Map<MineshaftItem, Long> lacking = new HashMap<MineshaftItem, Long>();
+ getIngredients().forEach((item, q) -> {
+ if (items.get(item) < q* amount) {
+ lacking.put(item, (q * amount) - items.get(item));
+ }
+ });
+
+ return Collections.unmodifiableMap(lacking);
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/Resource.java b/src/main/java/net/uomc/mineshaft/resources/Resource.java
new file mode 100644
index 0000000..1cd508e
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/Resource.java
@@ -0,0 +1,145 @@
+package net.uomc.mineshaft.resources;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.json.JSONObject;
+
+import com.mouldycheerio.dbot.util.DatabaseUtils;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Member;
+
+public class Resource extends JSONObject {
+
+ private static final String SYMBOL = "symbol";
+ private static final String NAME = "name";
+ private static final String VALUE = "value";
+ private ResourceManager resourceManager;
+ private String tableName;
+
+ public Resource(String name, String symbol, long value, ResourceManager resourceManager) {
+ super();
+ setName(name);
+ setSymbol(symbol);
+ setValue(value);
+ this.resourceManager = resourceManager;
+ initDB();
+
+ }
+
+ public Resource(JSONObject jsonObject, ResourceManager resourceManager) {
+ super();
+ this.resourceManager = resourceManager;
+ jsonObject.keySet().forEach(k -> put(k, jsonObject.get(k)));
+ initDB();
+ }
+
+ public void initDB() {
+ tableName = getName().split(" ")[0];
+ try {
+ DatabaseUtils.createSimpleKVtable(resourceManager.getDatabasepath(), tableName);
+ } catch (SQLException e) {
+ // e.printStackTrace();
+ }
+ }
+
+ public void set(Member member, long value) {
+ try {
+ DatabaseUtils.putInKVtable(resourceManager.getDatabasepath(), tableName, member.getId() + ":" + member.getGuild().getId(), value);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public long get(Member member) {
+ try {
+ return DatabaseUtils.getInKVtable(resourceManager.getDatabasepath(), tableName, member.getId() + ":" + member.getGuild().getId());
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0l;
+ }
+
+ public long getValue(String memberid, String guildId) {
+ try {
+ return DatabaseUtils.getInKVtable(resourceManager.getDatabasepath(), tableName, memberid + ":" + guildId);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0l;
+ }
+
+ public long total() {
+ try {
+ return DatabaseUtils.getTotal(resourceManager.getDatabasepath(), tableName);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+
+ public List<String> getMembersGuilds() {
+ try {
+ return DatabaseUtils.listKeys(resourceManager.getDatabasepath(), tableName);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return Collections.emptyList();
+ }
+
+ public List<String> getMembers(Guild guild) {
+ return getMembersGuilds().stream().filter(m -> guild.getId().equals(m.split(":")[1])).map(m -> m.split(":")[0]).collect(Collectors.toList());
+ }
+
+ public void increment(Member member, long amount) {
+ set(member, get(member) + amount);
+ }
+
+ public String getName() {
+ return getString(NAME);
+ }
+
+ public String getPrettyName() {
+ return Arrays.stream(getString(NAME).replaceAll("_", " ").split("\\s+"))
+ .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1))
+ .collect(Collectors.joining(" "));
+ }
+
+ public void setName(String name) {
+ put(NAME, name);
+ }
+
+ public String getSymbol() {
+ return getString(SYMBOL);
+ }
+
+ public void setSymbol(String symbol) {
+ put(SYMBOL, symbol);
+ }
+
+ public long getValue() {
+ if (total() <= 0) {
+ return -1;
+ } else {
+ return resourceManager.getPrimaryResource().total() / total();
+ }
+ }
+
+ public void setValue(long value) {
+ put(VALUE, value);
+ }
+
+ public String prettyValue(long value) {
+ return PeelingUtils.amountToString(value) + getSymbol();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java b/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java
new file mode 100644
index 0000000..8c58ab3
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java
@@ -0,0 +1,224 @@
+package net.uomc.mineshaft.resources;
+
+import java.io.File;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandController;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+import net.uomc.mineshaft.resources.commands.CreateResourceCommand;
+import net.uomc.mineshaft.resources.commands.GiveResourcesCommand;
+import net.uomc.mineshaft.resources.commands.LeaderBoardCommand;
+import net.uomc.mineshaft.resources.commands.ListResourcesCommand;
+import net.uomc.mineshaft.resources.commands.MyResourcesCommand;
+import net.uomc.mineshaft.resources.commands.RemoveResourceCommand;
+import net.uomc.mineshaft.resources.commands.SetPrimaryResourceCommand;
+import net.uomc.mineshaft.resources.commands.ValuesCommand;
+import net.uomc.mineshaft.resources.market.MarketCommand;
+import net.uomc.mineshaft.resources.market.MarketManager;
+
+public class ResourceManager {
+
+ private static final String PRIMARY_RESOURCE_KEY = "primary_resource";
+ private static final String RESOURCES_KEY = "resources";
+ private List<Resource> resources = new ArrayList<Resource>();
+
+ private File configFile;
+ private CustomBot bot;
+ private String databasepath;
+
+ private String primaryResource = "money";
+
+ private MarketManager marketManager;
+
+ public ResourceManager(CustomBot customBot) {
+ this.bot = customBot;
+ configFile = new File(customBot.getDatadir(), "resources_config.json");
+
+ File database = new File(customBot.getDatadir(), "resources.db");
+ databasepath = database.getPath();
+
+ loadResources();
+
+ marketManager = new MarketManager(this);
+
+ initCommands();
+ }
+
+ private void initCommands() {
+ CommandController cc = bot.getCommandController();
+
+ // setup commands
+ cc.addCommand(new ListResourcesCommand(this));
+ cc.addCommand(new CreateResourceCommand(this));
+ cc.addCommand(new RemoveResourceCommand(this));
+ cc.addCommand(new SetPrimaryResourceCommand(this));
+
+ MyResourcesCommand myResourcesCommand = new MyResourcesCommand(this);
+
+ cc.addCommand(CommandDetails.from("balance,bal,$,money"), (e, b, args) -> {
+ Member member = PeelingUtils.getSingleMentionFromArgs(e);
+
+ bot.sendMessage(
+ e, (member.equals(e.getMember()) ? "You have" : member.getEffectiveName() + " has")
+ + " **" + getPrimaryResource().prettyValue(
+ getPrimaryResource().get(member)
+ ) + "**"
+ );
+
+ });
+
+ // public commands
+ cc.addCommand(myResourcesCommand);
+
+ cc.addCommand(new GiveResourcesCommand(this));
+ //cc.addCommand(new MarketCommand(marketManager));
+
+ cc.addCommand(new ValuesCommand(this));
+ cc.addCommand(new LeaderBoardCommand(this));
+ }
+
+ private void loadResources() {
+ JSONObject loadJSON = PeelingUtils.loadJSON(configFile);
+
+ if (loadJSON.has(RESOURCES_KEY)) {
+ JSONArray jsonArray = loadJSON.getJSONArray(RESOURCES_KEY);
+ for (Object o : jsonArray) {
+ if (o instanceof JSONObject) {
+ Resource resource = new Resource((JSONObject) o, this);
+ resources.add(resource);
+ }
+ }
+ }
+
+ if (loadJSON.has(PRIMARY_RESOURCE_KEY)) {
+ setPrimaryResource(loadJSON.getString(PRIMARY_RESOURCE_KEY));
+ }
+ }
+
+ private void saveResources() {
+ JSONObject save = new JSONObject();
+ save.put(PRIMARY_RESOURCE_KEY, primaryResource);
+
+ resources.forEach(r -> save.append(RESOURCES_KEY, r));
+
+ PeelingUtils.saveJSONPretty(configFile, save);
+ }
+
+ public void addResource(Resource resource) {
+ resources.add(resource);
+ saveResources();
+ }
+
+ public Resource getResource(String input) {
+ for (Resource resource : resources) {
+ if (resource.getName().equalsIgnoreCase(input) || resource.getSymbol().equalsIgnoreCase(input)) {
+ return resource;
+ }
+ }
+ return null;
+ }
+
+ public List<Resource> listResources() {
+ return Collections.unmodifiableList(resources);
+ }
+
+ public boolean removeResource(String name) {
+ Resource resource = getResource(name);
+ if (resource != null) {
+ resources.remove(resource);
+ saveResources();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public CustomBot getBot() {
+ return bot;
+ }
+
+ public void setBot(CustomBot bot) {
+ this.bot = bot;
+ }
+
+ public String getDatabasepath() {
+ return databasepath;
+ }
+
+ public void setDatabasepath(String databasepath) {
+ this.databasepath = databasepath;
+ }
+
+ public Resource getPrimaryResource() {
+ Resource resource = getResource(primaryResource);
+ return resource == null ? resources.get(0) : resource;
+
+ }
+
+ public String getPrimaryResourceName() {
+ return primaryResource;
+ }
+
+ public void setPrimaryResource(String primaryResource) {
+ this.primaryResource = primaryResource;
+ saveResources();
+ }
+
+ public List<Resource> getResources() {
+ return Collections.unmodifiableList(resources);
+ }
+
+ public long getResource(Member member, String resourceName) {
+ Resource resource = getResource(resourceName);
+ if (resource == null)
+ return 0;
+
+ return resource.get(member);
+ }
+ public void setResource(Member member, String resourceName, long amount) {
+ Resource resource = getResource(resourceName);
+ if (resource == null)
+ return;
+
+ resource.set(member, amount);
+ }
+
+ public void addResource(Member member, String resource, long amount){
+ long a = getResource(member, resource);
+ setResource(member, resource, a + amount);
+ }
+
+ public void addResources(Member member, Map<String, Long> resources) {
+ resources.forEach((k, v) -> {
+ addResource(member, k, v);
+ });
+ }
+
+ public String createResourceList(Map<String, Long> resources) {
+ StringBuilder string = new StringBuilder();
+ resources.entrySet().stream()
+ .sorted((e1, e2) ->
+ (int)(e2.getValue() - e1.getValue()))
+ .forEach((e) -> {
+ Resource resource = getResource(e.getKey());
+ if (resource != null)
+ string.append(resource.prettyValue(e.getValue()) + "\n");
+ });
+ return string.toString();
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/ResourceTable.java b/src/main/java/net/uomc/mineshaft/resources/ResourceTable.java
new file mode 100644
index 0000000..8cce03e
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/ResourceTable.java
@@ -0,0 +1,127 @@
+package net.uomc.mineshaft.resources;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONObject;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+
+public class ResourceTable extends JSONObject {
+ public ResourceTable(JSONObject jsonObject) {
+ super();
+ jsonObject.keySet().forEach(k -> put(k, jsonObject.get(k)));
+ }
+
+ public ResourceTable() {
+ super();
+ }
+
+ public void setResource(Resource resource, long value) {
+ put(resource.getName(), value);
+ }
+
+ public long getResource(Resource resource) {
+ String name = resource.getName();
+ if (has(name)) {
+ return getLong(name);
+ }
+ return 0l;
+ }
+
+ public Map<Resource, Long> getTable(ResourceManager resourceManager) {
+ HashMap<Resource, Long> hashMap = new HashMap<Resource, Long>();
+ Iterator<String> keys = keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Resource resource = resourceManager.getResource(key);
+ if (resource != null) {
+ long q = getLong(key);
+ hashMap.put(resource, q);
+ }
+ }
+
+ return Collections.unmodifiableMap(hashMap);
+ }
+
+ public void addToUser(Member user, ResourceManager resourceManager) {
+ Map<Resource, Long> table = getTable(resourceManager);
+ table.forEach((resource, q) -> {
+ resource.increment(user, q);
+ });
+ }
+
+ public void removeFromUser(Member user, ResourceManager resourceManager) {
+ Map<Resource, Long> table = getTable(resourceManager);
+ table.forEach((resource, q) -> {
+ resource.increment(user, -q);
+ });
+ }
+
+ /**
+ * @param user
+ * @param resourceManager
+ * @return list of resources that are lacking
+ */
+ public Map<Resource, Long> doesUserHave(Member user, ResourceManager resourceManager) {
+ Map<Resource, Long> table = getTable(resourceManager);
+ Map<Resource, Long> lacking = new HashMap<Resource, Long>();
+ table.forEach((resource, q) -> {
+ if (resource.get(user) < q) {
+ lacking.put(resource, q - resource.get(user));
+ }
+ });
+
+ return Collections.unmodifiableMap(lacking);
+ }
+
+ public String toStringList(ResourceManager resourceManager) {
+ Map<Resource, Long> table = getTable(resourceManager);
+
+ List<String> parts = new ArrayList<String>();
+ table.forEach((r, q) -> parts.add(r.prettyValue(q)));
+
+ return String.join(", ", parts);
+ }
+
+ public void addResourceTable(ResourceTable table) {
+ Iterator<String> keys = table.keys();
+ while (keys.hasNext()) {
+ String next = keys.next();
+
+ put(next, optLong(next) + table.getLong(next));
+ }
+ }
+
+ public ResourceTable negatives() {
+ ResourceTable negatives = new ResourceTable();
+ Iterator<String> keys = keys();
+ while (keys.hasNext()) {
+ String next = keys.next();
+ long long1 = getLong(next);
+ if (long1 < 0) {
+ negatives.put(next, Math.abs(long1));
+ }
+ }
+ return negatives;
+ }
+
+ public ResourceTable postives() {
+ ResourceTable positives = new ResourceTable();
+ Iterator<String> keys = keys();
+ while (keys.hasNext()) {
+ String next = keys.next();
+ long long1 = getLong(next);
+ if (long1 > 0) {
+ positives.put(next, long1);
+ }
+ }
+ return positives;
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/CreateResourceCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/CreateResourceCommand.java
new file mode 100644
index 0000000..a9f0dc6
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/CreateResourceCommand.java
@@ -0,0 +1,44 @@
+package net.uomc.mineshaft.resources.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class CreateResourceCommand extends ResourceCommand {
+ private List<String> questions;
+
+ public CreateResourceCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.hidden("createItem"));
+
+ questions = new ArrayList<String>();
+ questions.add("What is the name of this item?");
+ questions.add("What is the symbol for this item?");
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ String ownerid = op.getConfig().getString("ownerid");
+ if (ownerid.equals(e.getAuthor().getId())) {
+ PeelingUtils.askQuestions(e, questions, (responses) -> {
+ String name = responses.get(0);
+ String symbol = responses.get(1);
+
+ Resource resource = new Resource(name, symbol, 1, getResourceManager());
+ getResourceManager().addResource(resource);
+
+ op.sendMessage(e, "Items", ":white_check_mark: added **" + name + "** to the list of items");
+ });
+ } else {
+ op.sendErrorMessage(e, ":x: this command is not intended for you");
+ }
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/GiveResourcesCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/GiveResourcesCommand.java
new file mode 100644
index 0000000..a98a452
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/GiveResourcesCommand.java
@@ -0,0 +1,85 @@
+package net.uomc.mineshaft.resources.commands;
+
+import static java.lang.Math.abs;
+
+import java.util.Optional;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class GiveResourcesCommand extends ResourceCommand {
+
+ private String usage;
+
+ public GiveResourcesCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.from("give,trade", "Give a user an item", "give [@user] [quantity] [item]"));
+ usage = "\n`" + getCommandDetails().getUsage() + "`";
+
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ if (args.length > 1) {
+ String usermention = args[0];
+ String quantitystring = args[1];
+ String resourcename = getResourceManager().getPrimaryResourceName();
+ if (args.length > 2) {
+ resourcename = args[2];
+ }
+
+ boolean generate = false;
+ String ownerid = op.getConfig().getString("ownerid");
+ if (ownerid.equals(e.getAuthor().getId())) {
+ if (args.length > 3) {
+ if (args[3].equals("generate")) {
+ generate = true;
+ }
+ }
+ }
+ Optional<Member> mentionToUser = PeelingUtils.mentionToMember(usermention, e.getGuild());
+ if (mentionToUser.isPresent()) {
+ Member member = mentionToUser.get();
+ try {
+ long quantity = Long.parseLong(quantitystring);
+ quantity = abs(quantity);
+
+ Resource resource = getResourceManager().getResource(resourcename);
+ if (resource != null) {
+
+ if (!generate && quantity > resource.get(e.getMember())) {
+ op.sendErrorMessage(e,":x: You do not have enough " + resource.getName() + "!");
+ } else {
+ if (!generate) {
+ resource.increment(e.getMember(), -quantity);
+ }
+ resource.increment(member, quantity);
+ op.sendSuccessMessage(e, ":white_check_mark: You gave " + member.getAsMention() + " " + resource.prettyValue(quantity));
+ }
+ } else {
+ op.sendErrorMessage(e, ":x: Please provide a valid resource!" + usage);
+
+ }
+
+ } catch (NumberFormatException ex) {
+ op.sendErrorMessage(e,":x: Please provide a valid quantity!" + usage);
+
+ }
+
+ } else {
+ op.sendErrorMessage(e, ":x: That user was not found!" + usage);
+ }
+ } else {
+ op.sendErrorMessage(e, ":x: Invalid usage!" + usage);
+
+ }
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/LeaderBoardCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/LeaderBoardCommand.java
new file mode 100644
index 0000000..a001577
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/LeaderBoardCommand.java
@@ -0,0 +1,50 @@
+package net.uomc.mineshaft.resources.commands;
+
+import static java.util.stream.Collectors.toMap;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class LeaderBoardCommand extends ResourceCommand {
+
+ public LeaderBoardCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(
+ CommandDetails.from("leaderboard,lb,top,best", "Lists the most valuable users", "top [@user]"));
+
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ AtomicInteger n = new AtomicInteger(0);
+ op.sendMessage(
+ e, "Most Valuable Players", "*ranked by inventory value*\n\n" + String.join(
+ "\n", getResourceManager().getPrimaryResource().getMembers(e.getGuild()).stream().collect(
+ toMap(
+ Function.identity(),
+ user -> getResourceManager()
+ .getResources()
+ .stream()
+ .map(r -> r.getValue(user, e.getGuild().getId()) * r.getValue())
+ .reduce((long) 0, (a, b) -> a + b)))
+ .entrySet().stream()
+ .sorted(
+ (o1, o2) -> Long.compare(o2.getValue(), o1.getValue()))
+ .limit(10)
+ .map(
+ entry -> String.format(
+ "%d <@%s> (**%s**)",
+ n.incrementAndGet(),
+ entry.getKey(),
+ PeelingUtils.amountToString(entry.getValue())))
+ .collect(Collectors.toList())));
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/ListResourcesCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/ListResourcesCommand.java
new file mode 100644
index 0000000..cb6c33a
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/ListResourcesCommand.java
@@ -0,0 +1,31 @@
+package net.uomc.mineshaft.resources.commands;
+
+import java.util.List;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class ListResourcesCommand extends ResourceCommand {
+
+ public ListResourcesCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.hidden("listItems"));
+
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ List<Resource> resources = getResourceManager().listResources();
+ StringBuilder stringBuilder = new StringBuilder();
+ resources.forEach(r -> {
+ stringBuilder.append(r.getSymbol() + " " + r.getName() + "\n");
+ });
+
+ op.sendMessage(e, "Available Items", stringBuilder.toString());
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/MyResourcesCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/MyResourcesCommand.java
new file mode 100644
index 0000000..b172a1e
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/MyResourcesCommand.java
@@ -0,0 +1,53 @@
+package net.uomc.mineshaft.resources.commands;
+
+import java.util.List;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class MyResourcesCommand extends ResourceCommand {
+
+ public MyResourcesCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.from("items,inventory,inv,i", "Lists your current inventory of items", "inventory [@user]"));
+
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ List<Resource> resources = getResourceManager().listResources();
+
+ Member member = PeelingUtils.getSingleMentionFromArgs(e);
+ boolean your = member.equals(e.getMember());
+ StringBuilder stringBuilder = new StringBuilder();
+ resources.stream().sorted((r1, r2) -> (int)(r1.get(member) - r2.get(member))).forEach(r -> {
+ long l = r.get(member);
+ if (l != 0) {
+ String v = r.prettyValue(l);
+ stringBuilder.append("**" + v + "** " + r.getName());
+ stringBuilder.append("\n");
+ }
+ });
+ String name = (your ? "Your" : member.getEffectiveName() + "'s");
+ if (stringBuilder.length() == 0)
+ stringBuilder.append(name + "inventory is empty");
+
+ op.sendMessage(e, name + " Inventory", stringBuilder.toString() + "\n*Total value: " + PeelingUtils.amountToString(calculateInventoryValue(member)) + "*");
+ }
+
+ public long calculateInventoryValue(Member member) {
+ return getResourceManager()
+ .getResources()
+ .stream()
+ .map(r -> r.get(member) * r.getValue())
+ .reduce((long) 0, (a, b) -> a + b);
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/RemoveResourceCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/RemoveResourceCommand.java
new file mode 100644
index 0000000..2fadf27
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/RemoveResourceCommand.java
@@ -0,0 +1,46 @@
+package net.uomc.mineshaft.resources.commands;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.Command;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class RemoveResourceCommand extends ResourceCommand implements Command {
+
+ public RemoveResourceCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.hidden("removeItem"));
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ String ownerid = op.getConfig().getString("ownerid");
+ if (ownerid.equals(e.getAuthor().getId())) {
+ if (args.length > 0) {
+ String name = args[0];
+ PeelingUtils.askQuestion(e, "Are you sure you want to delete **" + name + "**?\n(warning: all userdata will be lost)\nSend `confirm` to proceed", r -> {
+ if (r.equalsIgnoreCase("confirm")) {
+ boolean removeResource = getResourceManager().removeResource(name);
+ if (removeResource) {
+ op.sendMessage(e, "removed", "removed the item **" + name + "** from the list of items");
+
+ } else {
+ op.sendMessage(e, "not found", ":x: the item **" + name + "** was not found");
+ }
+ } else {
+ op.sendMessage(e, "cancelled", ":x: operation cancelled");
+
+ }
+ });
+ } else {
+ op.sendMessage(e, "incorrect usage", ":x: usage: `.removeResource [name]`");
+ }
+ } else {
+ op.sendMessage(e, "missing permissions", ":x: this command is not intended for you");
+ }
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/ResourceCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/ResourceCommand.java
new file mode 100644
index 0000000..9d99e0e
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/ResourceCommand.java
@@ -0,0 +1,17 @@
+package net.uomc.mineshaft.resources.commands;
+
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public abstract class ResourceCommand extends DetailedCommand {
+ private ResourceManager resourceManager;
+
+ public ResourceCommand(ResourceManager resourceManager) {
+ this.resourceManager = resourceManager;
+ }
+
+ public ResourceManager getResourceManager() {
+ return resourceManager;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/SetPrimaryResourceCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/SetPrimaryResourceCommand.java
new file mode 100644
index 0000000..6515d06
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/SetPrimaryResourceCommand.java
@@ -0,0 +1,47 @@
+package net.uomc.mineshaft.resources.commands;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class SetPrimaryResourceCommand extends ResourceCommand {
+
+ public SetPrimaryResourceCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.hidden("setPrimaryItem"));
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+ String ownerid = op.getConfig().getString("ownerid");
+ if (ownerid.equals(e.getAuthor().getId())) {
+ if (args.length > 0) {
+ Resource resource = getResourceManager().getResource(args[0]);
+ if (resource != null) {
+ PeelingUtils.askQuestion(e, "This will reset all users balance for this item, type `confirm` to continue", r -> {
+ if (r.equals("confirm")) {
+ getResourceManager().setPrimaryResource(resource.getName());
+
+ e.getGuild().loadMembers().onSuccess(members -> {
+ members.forEach(m -> getResourceManager().getPrimaryResource().set(m, 0));
+ });
+
+ op.sendMessage(e, "primary item", "Set **" + resource.getName() + "** to the primary item");
+ }
+ });
+ } else {
+ op.sendErrorMessage(e, "This item was not found");
+ }
+ } else {
+ op.sendMessage(e, "primary resource", "This will be the default item that will be used for trading\nUsage: `.setPrimaryResource [item name]`");
+ }
+ } else {
+ op.sendErrorMessage(e, ":x: this command is not intended for you");
+ }
+ }
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/commands/ValuesCommand.java b/src/main/java/net/uomc/mineshaft/resources/commands/ValuesCommand.java
new file mode 100644
index 0000000..c094d83
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/commands/ValuesCommand.java
@@ -0,0 +1,45 @@
+package net.uomc.mineshaft.resources.commands;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.CommandFail;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class ValuesCommand extends ResourceCommand {
+
+ public ValuesCommand(ResourceManager resourceManager) {
+ super(resourceManager);
+ setCommandDetails(CommandDetails.from("marketValue,values,prices", "Show estimated market values of all the items"));
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot b, String[] args) throws CommandFail {
+ long total = getResourceManager().getPrimaryResource().total();
+
+ List<Resource> resources = new ArrayList<Resource>(getResourceManager().getResources());
+ Collections.sort(resources, (r1, r2) -> Long.compare(r2.getValue(),r1.getValue()));
+
+ StringBuilder stringBuilder = new StringBuilder();
+ resources.forEach((r) -> {
+ long v = r.getValue();
+ stringBuilder.append("**1** " + r.getName() + r.getSymbol() + " : ");
+ if (v < 0) {
+ stringBuilder.append("**???**");
+ } else {
+ stringBuilder.append("**" + getResourceManager().getPrimaryResource().prettyValue(v) + "**");
+ }
+ stringBuilder.append("\n");
+ });
+
+ b.sendMessage(e, "Estimated Market Values", stringBuilder.toString());
+ }
+
+
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/market/MarketCommand.java b/src/main/java/net/uomc/mineshaft/resources/market/MarketCommand.java
new file mode 100644
index 0000000..47b0536
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/market/MarketCommand.java
@@ -0,0 +1,159 @@
+package net.uomc.mineshaft.resources.market;
+
+import java.util.List;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.commands.CommandDetails;
+import com.mouldycheerio.dbot.commands.DetailedCommand;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+import net.uomc.mineshaft.resources.market.Offer;
+
+public class MarketCommand extends DetailedCommand {
+ private ResourceManager resourceManager;
+ private MarketManager marketManager;
+
+ public MarketCommand(MarketManager marketManager) {
+ this.marketManager = marketManager;
+ this.resourceManager = marketManager.getResourceManager();
+ setCommandDetails(CommandDetails.from("market,m", "Buy and sell items at a given quantity on the market", "market sell [items] for [price]"));
+
+ }
+
+ @Override
+ public void execute(MessageReceivedEvent e, CustomBot op, String[] args) {
+
+ marketManager.processOffers();
+ if (args.length == 0) {
+ // TODO allow multipages with <-, ->
+
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setTitle("Market");
+ embedBuilder.setColor(op.color);
+ if (marketManager.getEmbedFields().size() == 0) {
+ embedBuilder.setDescription("**:x: There are no available offers!**\n" + "Create one with `market sell`");
+ e.getMessage().replyEmbeds(embedBuilder.build()).queue();
+ } else {
+ embedBuilder.setDescription("Use `" + op.getPrefixManager().getPrefix(e.getGuild()) + "market buy [id]` to accept an offer");
+ PeelingUtils.pagesEmbed(e.getAuthor(), e.getChannel(), embedBuilder, marketManager.getEmbedFields());
+ }
+
+ } else if (args.length > 0) {
+ if (args[0].equals("buy")) {
+ if (args.length > 1) {
+ buy(e, op, args);
+ } else {
+ op.sendErrorMessage(e,":x: please specify an id!");
+ }
+ } else if (args[0].equals("sell")) {
+ sell(e, op, args);
+ }
+ }
+ }
+
+ private void sell(MessageReceivedEvent e, CustomBot op, String[] args) {
+ if (args.length > 5) {
+
+ try {
+ long sellQ = Math.abs(Long.parseLong(args[1]));
+ long buyQ = Math.abs(Long.parseLong(args[4]));
+ String sellRname = args[2];
+ Resource sellR = resourceManager.getResource(sellRname);
+ String buyRname = args[5];
+ Resource buyR = resourceManager.getResource(buyRname);
+ if (sellR != null || buyR != null) {
+ if (sellR.get(e.getMember()) >= sellQ) {
+ Offer offer = new Offer();
+
+ offer.setUserID(e.getAuthor().getId());
+ offer.setBuyingQuantity(buyQ);
+ offer.setBuyingResource(buyR);
+ offer.setID(Offer.highest + 1);
+ offer.setSellingQuantity(sellQ);
+ offer.setSellingResource(sellR);
+
+ marketManager.addOffer(offer);
+ op.sendSuccessMessage(e, ":white_check_mark: successfully listed **" + offer.getAsString(resourceManager) + "** offer id: `" + offer.getID() + "`");
+ } else {
+ op.sendErrorMessage(e, ":x: You don't have enough " + sellR + " to offer");
+ }
+ } else {
+ sendSellUsage(e, op);
+ }
+ } catch (NumberFormatException ex) {
+ sendSellUsage(e, op);
+ }
+ } else {
+ sendSellUsage(e, op);
+ }
+ }
+
+ private void sendSellUsage(MessageReceivedEvent e, CustomBot op) {
+ List<Resource> listResources = resourceManager.listResources();
+ Resource resource1 = listResources.get(listResources.size() - 1);
+ Resource resource2 = listResources.get(0);
+
+ int q1 = (int) (Math.random() * 10);
+ int q2 = (int) (Math.random() * 10);
+ op.sendMessage(
+ e, "incorrect usage",
+ ":x: usage: `market sell [items] for [price]`\ni.e: `market sell " + q1 + " " + resource1.getName() + " for " + q2 + " " + resource2.getName() + "`"
+ );
+ }
+
+ private void buy(MessageReceivedEvent e, CustomBot op, String[] args) {
+ String id = args[1];
+ Offer offer = marketManager.getOffer(id);
+ if (offer != null) {
+ offer.getMember(e.getGuild(), seller -> {
+ if (seller.equals(e.getAuthor())) {
+ op.sendErrorMessage(e, ":x: you cannot accept your own offer");
+ } else {
+ long buyingQuantity = offer.getBuyingQuantity();
+ Resource buyingResource = offer.getBuyingResource(resourceManager);
+ if (buyingResource.get(e.getMember()) >= buyingQuantity) {
+
+ long sellingQuantity = offer.getSellingQuantity();
+ Resource sellingResource = offer.getSellingResource(resourceManager);
+
+ buyingResource.increment(e.getMember(), -buyingQuantity);
+ sellingResource.increment(e.getMember(), sellingQuantity);
+
+ if (seller != null) {
+ buyingResource.increment(seller, buyingQuantity);
+ sellingResource.increment(seller, -sellingQuantity);
+ }
+ seller.getUser().openPrivateChannel().queue(pc -> {
+ pc.sendMessage(
+ "**" + e.getAuthor().getAsTag() + "** has accepted your market offer: **" + offer.getAsString(resourceManager) + "** [" + offer.getID() + "]"
+ )
+ .queue();
+ });
+
+ String from = "";
+ if (seller != null) {
+ from = " from " + seller.getAsMention();
+ }
+
+ marketManager.removeOffer(offer);
+ op.sendSuccessMessage(
+ e, ":white_check_mark: successfully bought **" + sellingResource.prettyValue(sellingQuantity) + "** for **"
+ + buyingResource.prettyValue(buyingQuantity) + "**" + from
+ );
+
+ } else {
+ long have = buyingResource.get(e.getMember());
+ long more = buyingQuantity - have;
+ op.sendErrorMessage(e, ":x: you need **" + buyingResource.prettyValue(more) + "** more to buy this");
+ }
+ }
+ });
+ } else {
+ op.sendErrorMessage(e, ":x: please specify a valid id!");
+ }
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/market/MarketManager.java b/src/main/java/net/uomc/mineshaft/resources/market/MarketManager.java
new file mode 100644
index 0000000..05517aa
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/market/MarketManager.java
@@ -0,0 +1,128 @@
+package net.uomc.mineshaft.resources.market;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.json.JSONObject;
+
+import com.mouldycheerio.dbot.CustomBot;
+import com.mouldycheerio.dbot.util.PeelingUtils;
+
+import net.dv8tion.jda.api.entities.MessageEmbed.Field;
+import net.dv8tion.jda.api.entities.User;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+import net.uomc.mineshaft.resources.market.Offer;
+
+public class MarketManager {
+ private ResourceManager resourceManager;
+
+ private List<Offer> offers = new ArrayList<Offer>();
+
+ private File dataFile;
+
+ private CustomBot bot;
+
+ public MarketManager(net.uomc.mineshaft.resources.ResourceManager resourceManager) {
+ this.resourceManager = resourceManager;
+ bot = resourceManager.getBot();
+
+ dataFile = new File(resourceManager.getBot().getDatadir(), "market.json");
+
+ load();
+ }
+
+ public void load() {
+ JSONObject loadJSON = PeelingUtils.loadJSON(dataFile);
+ if (loadJSON.has("offers")) {
+ loadJSON.getJSONArray("offers").forEach(o -> {
+ if (o instanceof JSONObject) {
+ Offer offer = new Offer((JSONObject) o);
+ offers.add(offer);
+ }
+ });
+ }
+ }
+
+ public void save() {
+ JSONObject jsonObject = new JSONObject();
+ for (Offer offer : offers) {
+ jsonObject.append("offers", offer);
+ }
+
+ PeelingUtils.saveJSON(dataFile, jsonObject);
+ }
+
+ public Offer getOffer(String id) {
+ for (Offer offer : offers) {
+ if (id.equals(offer.getID() + "")) {
+ return offer;
+ }
+ }
+ return null;
+ }
+
+ public void addOffer(Offer offer) {
+ offers.add(offer);
+ processOffers();
+ }
+
+ public void processOffers() {
+ // TODO uhhh we should bring this back
+ //Iterator<Offer> iterator = offers.iterator();
+ //while (iterator.hasNext()) {
+ // Offer offer = iterator.next();
+
+ // bot.getClient().retrieveUserById(offer.getUserID()).queue(seller -> {
+ // long quantity = offer.getSellingQuantity();
+ // Resource resource = offer.getSellingResource(resourceManager);
+ // long l = resource.get(seller);
+ // if (l < quantity) {
+
+ // sendNoLongerValidMessage(offer, seller);
+ // iterator.remove();
+ // }
+ // });
+ //}
+
+ //save();
+ }
+
+ private void sendNoLongerValidMessage(Offer offer, User seller) {
+ seller.openPrivateChannel().queue(pc -> {
+ pc.sendMessage("Your market offer: **" + offer.getAsString(resourceManager) + "** [" + offer.getID() + "] is no longer valid because you no longer have enough resources to complete it.").queue();
+ });
+ }
+
+ public void removeOffer(Offer o) {
+ offers.remove(o);
+ save();
+ }
+
+ public List<Offer> listOffers() {
+ return Collections.unmodifiableList(offers);
+ }
+
+ public List<Field> getEmbedFields() {
+ return listOffers().stream().map(
+ o -> new Field(
+ "[" + o.getID() + "] "
+ + o.getAsString(resourceManager),
+ "<@" + o.getUserID() + ">", false
+ )
+ )
+ .collect(Collectors.toList());
+ }
+
+ public ResourceManager getResourceManager() {
+ return resourceManager;
+ }
+
+ public void setResourceManager(ResourceManager resourceManager) {
+ this.resourceManager = resourceManager;
+ }
+}
diff --git a/src/main/java/net/uomc/mineshaft/resources/market/Offer.java b/src/main/java/net/uomc/mineshaft/resources/market/Offer.java
new file mode 100644
index 0000000..9e01acd
--- /dev/null
+++ b/src/main/java/net/uomc/mineshaft/resources/market/Offer.java
@@ -0,0 +1,104 @@
+package net.uomc.mineshaft.resources.market;
+
+import java.util.function.Consumer;
+
+import org.json.JSONObject;
+
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.User;
+import net.uomc.mineshaft.resources.Resource;
+import net.uomc.mineshaft.resources.ResourceManager;
+
+public class Offer extends JSONObject {
+ private static final String ID = "id";
+ private static final String BUYING_QUANTITY = "buying_quantity";
+ private static final String SELLING_QUANTITY = "selling_quantity";
+ private static final String BUYING_RESOURCE = "buying_resource";
+ private static final String SELLING_RESOURCE = "selling_resource";
+ private static final String USERID = "userid";
+
+ public static int highest = 0;
+
+ public Offer() {
+ super();
+ }
+
+ public Offer(JSONObject jsonObject) {
+ super();
+ jsonObject.keySet().forEach(k -> put(k, jsonObject.get(k)));
+
+ if (has(ID)) {
+ if (getID() >= highest) {
+ highest = getID();
+ }
+ }
+ }
+
+ public int getID() {
+ return getInt(ID);
+ }
+
+ public void setID(int id) {
+ put(ID, id);
+ if (getID() >= highest) {
+ highest = getID();
+ }
+ }
+
+ public String getUserID() {
+ return getString(USERID);
+ }
+
+ public void getMember(Guild guild, Consumer<Member> c) {
+ c.accept(guild.getMemberById(getUserID()));
+ }
+
+ public Member getMember(Guild guild) {
+ return guild.getMemberById(getUserID());
+ }
+
+ public void setUserID(String userid) {
+ put(USERID, userid);
+ }
+
+ public Resource getSellingResource(ResourceManager resourceManager) {
+ String string = getString(SELLING_RESOURCE);
+ return resourceManager.getResource(string);
+ }
+
+ public void setSellingResource(Resource resource) {
+ put(SELLING_RESOURCE, resource.getName());
+ }
+
+ public Resource getBuyingResource(ResourceManager resourceManager) {
+ String string = getString(BUYING_RESOURCE);
+ return resourceManager.getResource(string);
+ }
+
+ public void setBuyingResource(Resource resource) {
+ put(BUYING_RESOURCE, resource.getName());
+ }
+
+ public long getSellingQuantity() {
+ return getLong(SELLING_QUANTITY);
+ }
+
+ public void setSellingQuantity(long q) {
+ put(SELLING_QUANTITY, q);
+ }
+
+ public long getBuyingQuantity() {
+ return getLong(BUYING_QUANTITY);
+ }
+
+ public void setBuyingQuantity(long q) {
+ put(BUYING_QUANTITY, q);
+ }
+
+ public String getAsString(ResourceManager resourceManager) {
+ return getSellingResource(resourceManager).prettyValue(getSellingQuantity()) + " for " + getBuyingResource(resourceManager).prettyValue(getBuyingQuantity());
+ }
+}