diff options
| author | davidovski <david@davidovski.xyz> | 2025-10-11 04:55:51 +0100 |
|---|---|---|
| committer | davidovski <david@davidovski.xyz> | 2025-10-11 04:55:51 +0100 |
| commit | 10b327e9f6441a8863227ce3e075a5a587654065 (patch) | |
| tree | 5e94ecc91290febacf5181d3596563a7a24bd010 /src/main/java/net/uomc/mineshaft | |
v0.3.1
Diffstat (limited to 'src/main/java/net/uomc/mineshaft')
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()); + } +} |
