From fff63aaea786a5f1c59bbf99c999a2aa7bb810e5 Mon Sep 17 00:00:00 2001 From: davidovski Date: Sun, 19 Oct 2025 16:16:05 +0100 Subject: Add farm, sleep and trade --- src/main/java/net/uomc/mineshaft/DropUtil.java | 136 ++++++++++ src/main/java/net/uomc/mineshaft/FishCommand.java | 5 + src/main/java/net/uomc/mineshaft/MineCommand.java | 8 +- src/main/java/net/uomc/mineshaft/Mineshaft.java | 95 ++++--- src/main/java/net/uomc/mineshaft/Pickaxes.java | 2 +- .../java/net/uomc/mineshaft/PlayerHealths.java | 164 ++++++++++++ src/main/java/net/uomc/mineshaft/RobCommand.java | 97 ++------ src/main/java/net/uomc/mineshaft/SleepCommand.java | 128 ++++++++++ .../net/uomc/mineshaft/farm/CompostCommand.java | 136 ++++++++++ src/main/java/net/uomc/mineshaft/farm/Crop.java | 21 ++ src/main/java/net/uomc/mineshaft/farm/Farm.java | 109 ++++++++ .../java/net/uomc/mineshaft/farm/FarmCommand.java | 277 +++++++++++++++++++++ .../java/net/uomc/mineshaft/farm/TradeCommand.java | 239 ++++++++++++++++++ .../uomc/mineshaft/resources/ResourceManager.java | 6 +- 14 files changed, 1311 insertions(+), 112 deletions(-) create mode 100644 src/main/java/net/uomc/mineshaft/DropUtil.java create mode 100644 src/main/java/net/uomc/mineshaft/PlayerHealths.java create mode 100644 src/main/java/net/uomc/mineshaft/SleepCommand.java create mode 100644 src/main/java/net/uomc/mineshaft/farm/CompostCommand.java create mode 100644 src/main/java/net/uomc/mineshaft/farm/Crop.java create mode 100644 src/main/java/net/uomc/mineshaft/farm/Farm.java create mode 100644 src/main/java/net/uomc/mineshaft/farm/FarmCommand.java create mode 100644 src/main/java/net/uomc/mineshaft/farm/TradeCommand.java (limited to 'src') diff --git a/src/main/java/net/uomc/mineshaft/DropUtil.java b/src/main/java/net/uomc/mineshaft/DropUtil.java new file mode 100644 index 0000000..dce4a16 --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/DropUtil.java @@ -0,0 +1,136 @@ +package net.uomc.mineshaft; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import com.mouldycheerio.dbot.util.EventWaiter; +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.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public class DropUtil { + private static final String FOLLOWING_STRING = "The following items have dropped:"; + private static final int TO_PICK = 3; + private static final double MAX_AMOUNT_PERCENT = 0.35; + private static final long DROP_DURATION_SECONDS = 90; + public static final String LOOT_STRING = "loot"; + + public static Map getDeathDrops(Mineshaft bot, Member member) { + Map drop = new HashMap<>(); + + Map inventory = bot.getInventory(member); + + int items = TO_PICK; + if (bot.getItem(member, MineshaftItem.XP) > 0) { + drop.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 -> { + drop.put(e.getKey(), + (long) Math.ceil(e.getValue() * (Math.random() * MAX_AMOUNT_PERCENT))); + }); + + return drop; + } + + public static void spawnDropEdit(Mineshaft bot, Message message, MessageEmbed embed, Map items) { + message.editMessageEmbeds(embed).setContent("").queue(m -> { + waitForPickUp(bot, embed, items, m); + }); + } + + private static void waitForPickUp(Mineshaft bot, MessageEmbed embed, + Map items, Message m) { + EventWaiter eventWaiter = new EventWaiter(); + m.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(), items); + EmbedBuilder em = new EmbedBuilder(embed); + String desc = embed.getDescription().split(FOLLOWING_STRING)[0]; + em.setDescription( desc + "\n" + e.getMember().getAsMention() + " claimed the loot!\n**" + + bot.createItemList(items, "+%s") + "**"); + m.editMessageEmbeds(em.build()).queue(); + cleanupDrop(m.getChannel(), m); + }, DROP_DURATION_SECONDS, TimeUnit.SECONDS, () -> { + m.delete().queue(); + cleanupDrop(m.getChannel(), m); + }); + } + + public static void spawnDrop(Mineshaft bot, MessageChannel channel, MessageEmbed embed, + Map items) { + channel.sendMessageEmbeds(embed).queue(m -> { + waitForPickUp(bot, embed, items, m); + }); + } + + public static MessageEmbed getNoDropEmbed(Mineshaft bot, String reason, String title) { + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle(title); + eb.getDescriptionBuilder().append(reason); + eb.getDescriptionBuilder().append("\nThey dropped nothing..."); + eb.setColor(bot.color); + return eb.build(); + } + + public static MessageEmbed getDropEmbed(Mineshaft bot, Map items, MessageEmbed embed) { + EmbedBuilder eb = new EmbedBuilder(embed); + if (!bot.isEmpty(items)) { + eb.getDescriptionBuilder().append("\n" + FOLLOWING_STRING + "\n\n"); + eb.getDescriptionBuilder().append(bot.createItemList(items)); + eb.getDescriptionBuilder().append("\nSay `" + LOOT_STRING + "` to loot these items.\nHurry you only have " + + PeelingUtils.formatTimeRelativeFromNow(DROP_DURATION_SECONDS * 1000l) + " to collect them!"); + } + return eb.build(); + } + + public static MessageEmbed getDropEmbed(Mineshaft bot, Map items, String description, + String title) { + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle(title); + eb.getDescriptionBuilder().append(description); + if (!bot.isEmpty(items)) { + eb.getDescriptionBuilder().append("\n" + FOLLOWING_STRING + "\n\n"); + eb.getDescriptionBuilder().append(bot.createItemList(items)); + 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); + return eb.build(); + } + + public static void cleanupDrop(MessageChannel channel, Message message) { + channel.getHistoryAfter(message, 100).queueAfter(1, TimeUnit.SECONDS, h -> { + ArrayList msgs = new ArrayList(); + 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/FishCommand.java b/src/main/java/net/uomc/mineshaft/FishCommand.java index 2eac218..37612ca 100644 --- a/src/main/java/net/uomc/mineshaft/FishCommand.java +++ b/src/main/java/net/uomc/mineshaft/FishCommand.java @@ -50,6 +50,11 @@ public class FishCommand extends CooldownCommand { @Override public boolean trigger(MessageReceivedEvent e) { + if (bot.getPickaxes().isNether(e.getMember())) { + bot.sendErrorMessage(e, ":x: You cannot fish in the nether"); + return false; + } + e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(REACTION_FISH))).queue(); long waitTime = MIN_WAIT_MS + (long) (Math.random() * (MAX_WAIT_MS - MIN_WAIT_MS)); diff --git a/src/main/java/net/uomc/mineshaft/MineCommand.java b/src/main/java/net/uomc/mineshaft/MineCommand.java index 730ae85..03207e3 100644 --- a/src/main/java/net/uomc/mineshaft/MineCommand.java +++ b/src/main/java/net/uomc/mineshaft/MineCommand.java @@ -138,7 +138,7 @@ public class MineCommand extends CooldownCommand { } public MineshaftItem getOre(Member m, boolean nether) { - int pickaxe = bot.getPickaxes().getPickaxeLevel(m); + int pickaxe = m == null ? Pickaxes.MAX_PICKAXE_LEVEL : bot.getPickaxes().getPickaxeLevel(m); double random = Math.random(); int maxOre = levelToMaxOre(pickaxe); @@ -146,7 +146,7 @@ public class MineCommand extends CooldownCommand { List list = oresList; if(nether) list = netherOresList; - double c = getEfficiencyCurve(m); + double c = m == null ? 2 : getEfficiencyCurve(m); int i = (int) Math.floor(Math.pow(random, c) * maxOre); String oreName = list.get(i); MineshaftItem ore = MineshaftItem.valueOf(oreName.toUpperCase()); @@ -325,6 +325,10 @@ public class MineCommand extends CooldownCommand { default: break; } + + if (member == null) + return award; + return bot.multiply(award, getPickaxeFortuneCurve(member)); } diff --git a/src/main/java/net/uomc/mineshaft/Mineshaft.java b/src/main/java/net/uomc/mineshaft/Mineshaft.java index 1584529..41e4ce4 100644 --- a/src/main/java/net/uomc/mineshaft/Mineshaft.java +++ b/src/main/java/net/uomc/mineshaft/Mineshaft.java @@ -10,7 +10,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import javax.security.auth.login.LoginException; @@ -31,6 +33,7 @@ import com.mouldycheerio.dbot.starboard.StarboardController; import com.mouldycheerio.dbot.util.PeelingUtils; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.uomc.mineshaft.resources.ResourceManager; import net.uomc.mineshaft.resources.market.MarketCommand; @@ -43,11 +46,13 @@ public class Mineshaft extends CustomBot { private Crafting crafting; private MineCommand mineCommand; private EnchantCommand enchantCommand; + private PlayerHealths healths; public Mineshaft(JSONObject config) throws LoginException, JSONException, IOException, InterruptedException { super(config); pickaxes = new Pickaxes(this); + healths = new PlayerHealths(this); crafting = new Crafting(this); getCommandController().addCommand(crafting); @@ -68,6 +73,7 @@ public class Mineshaft extends CustomBot { getCommandController().addCommand(new PickaxeCommand(this)); getCommandController().addCommand(new Portal(this)); + getCommandController().addCommand(new SleepCommand(this)); mineCommand = new MineCommand(this); getCommandController().addCommand(mineCommand); @@ -154,36 +160,9 @@ public class Mineshaft extends CustomBot { }); 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; - } + String cooldowns = getCooldownsString(e.getMember()); - 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()); + b.sendMessage(e, "Active cooldowns", cooldowns); }); getCommandController().removeCommand("top"); @@ -198,6 +177,48 @@ public class Mineshaft extends CustomBot { logger = new FullLogger(this); } + public String getCooldownsString(Member member) { + return getCooldownsString(member, List.of()); + } + + public String getCooldownsString(Member member, List omit) { + StringBuilder cooldowns = new StringBuilder(); + getCommandController().getCommands().entrySet().stream().map((x) -> x.getValue()) + .filter(c -> c instanceof CooldownCommand) + .map(c -> (CooldownCommand) c) + .filter(c -> !omit.contains(c.getName())) + .sorted((c1, c2) -> { + return Long.compareUnsigned(c1.getCooldownController().getNextTime(member, c1.getName()), + c2.getCooldownController().getNextTime(member, c2.getName())); + }) + .forEach(c -> { + long nextCooldownTime = ((CooldownCommand) c).getCooldownController() + .getNextTime(member, ((CooldownCommand) c).getName()); + if (nextCooldownTime == 0) + return; + String timeLeft = PeelingUtils.formatTimeRelative(nextCooldownTime); + + if (System.currentTimeMillis() > nextCooldownTime) { + cooldowns.append("**"); + cooldowns.append(((CooldownCommand) c).getName()); + cooldowns.append("**\t\t"); + cooldowns.append(":white_check_mark:"); + cooldowns.append("\n"); + return; + } + + cooldowns.append("**"); + cooldowns.append(((CooldownCommand) c).getName()); + cooldowns.append("**\t\t"); + cooldowns.append(timeLeft); + cooldowns.append("\n"); + }); + if (cooldowns.length() == 0) { + cooldowns.append("You have no active cooldowns"); + } + return cooldowns.toString(); + } + public StarboardController getStarboardController() { return starboardController; } @@ -226,8 +247,8 @@ public class Mineshaft extends CustomBot { resourceManager.addResource(member, item.toString(), amount); } - public void addItems(Member member, Map items) { - resourceManager.addResources(member, items.entrySet().stream() + public long addItems(Member member, Map items) { + return resourceManager.addResources(member, items.entrySet().stream() .collect(Collectors.toMap( e -> e.getKey().toString(), Map.Entry::getValue @@ -251,8 +272,8 @@ public class Mineshaft extends CustomBot { resourceManager.addResource(member, getItem(item).getName(), -amount); } - public void removeItems(Member member, Map items) { - addItems(member, items.entrySet().stream().collect(Collectors.toMap( + public long removeItems(Member member, Map items) { + return addItems(member, items.entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> -e.getValue() ))); @@ -330,4 +351,12 @@ public class Mineshaft extends CustomBot { public EnchantCommand getEnchantCommand() { return enchantCommand; } + + public PlayerHealths getHealths() { + return healths; + } + + public boolean isEmpty(Map items) { + return items.entrySet().stream().mapToLong(e -> e.getValue()).sum() == 0; + } } diff --git a/src/main/java/net/uomc/mineshaft/Pickaxes.java b/src/main/java/net/uomc/mineshaft/Pickaxes.java index ca0fb34..39fa030 100644 --- a/src/main/java/net/uomc/mineshaft/Pickaxes.java +++ b/src/main/java/net/uomc/mineshaft/Pickaxes.java @@ -16,7 +16,7 @@ import net.dv8tion.jda.api.entities.Member; public class Pickaxes { - private static final int MAX_PICKAXE_LEVEL = 5; + public static final int MAX_PICKAXE_LEVEL = 5; private static final String LEVEL_TABLE = "levels"; private static final String FORTUNE_TABLE = "ench_fortune"; diff --git a/src/main/java/net/uomc/mineshaft/PlayerHealths.java b/src/main/java/net/uomc/mineshaft/PlayerHealths.java new file mode 100644 index 0000000..395f71a --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/PlayerHealths.java @@ -0,0 +1,164 @@ +package net.uomc.mineshaft; + +import java.io.File; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.mouldycheerio.dbot.commands.CommandDetails; +import com.mouldycheerio.dbot.util.DatabaseUtils; +import com.mouldycheerio.dbot.util.PeelingUtils; +import com.vdurmont.emoji.EmojiManager; +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.events.message.MessageReceivedEvent; + +public class PlayerHealths { + + private static final String HP_TABLE = "hp"; + private static final String HP_EMOJI = "<:hp:1428837195329962115>"; + private String healthDb; + private Mineshaft bot; + + private static final long MAX_HP = 20; + + public PlayerHealths(Mineshaft bot) { + + this.bot = bot; + healthDb = (new File(bot.getDatadir(), "players.db")).getPath(); + initDB(); + + bot.getCommandController().addCommand(CommandDetails.from("hp,health"), (e, b, args) -> { + Member member = PeelingUtils.getSingleMentionFromArgs(e); + b.sendMessage(e, member.getEffectiveName() + "'s health", (member.equals(e.getMember()) ? "Your " : member.getEffectiveName() + "'s") + " health: " + getHPString(member)); + }); + } + + public void setHP(Member member, long value) { + try { + DatabaseUtils.putInKVtable(healthDb, HP_TABLE, member.getId() + ":" + member.getGuild().getId(), value); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private long getHP(String member, String guild) { + try { + return DatabaseUtils.getInKVtable(healthDb, HP_TABLE, member + ":" + guild); + } catch (SQLException e) { + e.printStackTrace(); + } + return MAX_HP; + } + + public long getHP(Member member) { + return getHP(member.getId(), member.getGuild().getId()); + } + + public String getHPString(Member member) { + return getHPString(getHP(member)); + } + + public static String getHPString(long hp) { + return "**" + hp + HP_EMOJI + "**"; + } + + public long damage(MessageReceivedEvent event, Member member, long amount, String reason) { + long hp = getHP(member); + hp -= amount; + if (hp <= 0) { + killPlayer(event, member, reason); + setHP(member, MAX_HP); + } else { + setHP(member, hp); + } + return hp; + } + + public long damageAndDropIfKill(MessageReceivedEvent event, Member member, long amount, String reason, Map items) { + long hp = getHP(member); + hp -= amount; + if (hp <= 0) { + killPlayerAndDrop(event, member, reason, items); + setHP(member, MAX_HP); + } else { + setHP(member, hp); + } + return hp; + } + + public long damageAndDropIfKill(Message message, Member member, long amount, String reason, Map items) { + long hp = getHP(member); + hp -= amount; + if (hp <= 0) { + killPlayerAndDrop(message, member, reason, items); + setHP(member, MAX_HP); + } else { + setHP(member, hp); + } + return hp; + } + + public long damage(Message message, Member member, long amount, String reason) { + long hp = getHP(member); + hp -= amount; + if (hp <= 0) { + killPlayer(message, member, reason); + setHP(member, MAX_HP); + } else { + setHP(member, hp); + } + return hp; + } + + private void initDB() { + try { + DatabaseUtils.createSimpleKVtable(healthDb, HP_TABLE); + } catch (SQLException e) { + } + } + + public void killPlayerAndDrop(Message message, Member member, String deathMessage, Map items) { + + Map drops = DropUtil.getDeathDrops(bot, member); + drops = bot.sumItems(drops, items); + + bot.removeItems(member, drops); + + if (message != null && !message.getEmbeds().isEmpty()) { + EmbedBuilder builder = new EmbedBuilder(message.getEmbeds().getFirst()); + builder.appendDescription("\n" + deathMessage + "\n"); + MessageEmbed embed = DropUtil.getDropEmbed(bot, drops, builder.build()); + DropUtil.spawnDropEdit(bot, message, embed, drops); + return; + } + String title = getDeathTitle(member, deathMessage); + + MessageEmbed embed = DropUtil.getDropEmbed(bot, drops, deathMessage, title); + DropUtil.spawnDrop(bot, message.getChannel(), embed, drops); + } + + public void killPlayerAndDrop(MessageReceivedEvent event, Member member, String deathMessage, Map items) { + killPlayerAndDrop((Message) null, member, deathMessage, items); + } + + public void killPlayer(MessageReceivedEvent event, Member member, String deathMessage ) { + killPlayerAndDrop(event, member, deathMessage, new HashMap<>()); + } + public void killPlayer(Message message, Member member, String deathMessage ) { + killPlayerAndDrop(message, member, deathMessage, new HashMap<>()); + } + + private String getDeathTitle(Member member, String deathMessage) { + String title = member.getEffectiveName() + " has died!"; + if (EmojiManager.containsEmoji(deathMessage)) { + title = EmojiParser.extractEmojis(deathMessage).getFirst(); + } + return title; + } +} diff --git a/src/main/java/net/uomc/mineshaft/RobCommand.java b/src/main/java/net/uomc/mineshaft/RobCommand.java index 9ff8a12..ace95e7 100644 --- a/src/main/java/net/uomc/mineshaft/RobCommand.java +++ b/src/main/java/net/uomc/mineshaft/RobCommand.java @@ -27,12 +27,14 @@ 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.35; private static final long DROP_DURATION_SECONDS = 90; private static final String LOOT_STRING = "loot"; + + private static final long MAX_DAMAGE = 20; + private Mineshaft bot; private @NotNull String embedTitle; + private @NotNull String pvpEmoji; protected RobCommand(Mineshaft bot) { super(bot); @@ -40,6 +42,7 @@ public class RobCommand extends CooldownCommand { this.bot = bot; setDetails(CommandDetails.from("kill,pvp,rob", "kill another player")); embedTitle = EmojiParser.parseToUnicode(":crossed_swords:"); + pvpEmoji = EmojiParser.parseToUnicode(":crossed_swords:"); } @Override @@ -62,86 +65,30 @@ public class RobCommand extends CooldownCommand { return false; } - String response = target.getAsMention() + " was slain by " + e.getAuthor().getAsMention(); - - Map rob = getRob(target); + e.getMessage().addReaction(Emoji.fromUnicode(pvpEmoji)).queue(); 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.setTitle(pvpEmoji); + eb.getDescriptionBuilder().append(e.getAuthor().getAsMention() + " is fighting " + target.getAsMention() + "..." ); eb.setColor(bot.color); - e.getMessage().addReaction(Emoji.fromUnicode(EmojiParser.parseToUnicode(":crossed_swords:"))).queue(); - spawnDrop(e, eb.build(), target, rob); + long damage = getDamage(e.getMember()); + String damageString = " (" + PlayerHealths.getHPString(-damage) + ")"; + e.getMessage().replyEmbeds(eb.build()).delay(5, TimeUnit.SECONDS, e.getJDA().getRateLimitPool()).flatMap(m -> { + String deathMessage = pvpEmoji + target.getAsMention() + " was slain by " + e.getAuthor().getAsMention() + + damageString; + long hp = bot.getHealths().damage(m, target, damage, deathMessage); + return hp > 0; + }, m -> { + eb.setDescription(e.getAuthor().getAsMention() + " attacked " + target.getAsMention() + damageString + + "\n\n" + "They are now on " + bot.getHealths().getHPString(target)); + return m.editMessageEmbeds(eb.build()); + }).queue(); return true; } - public Map getRob(Member member) { - Map rob = new HashMap<>(); - - Map 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 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 msgs = new ArrayList(); - h.getRetrievedHistory().forEach(m -> { - if (m.getContentRaw().equalsIgnoreCase(LOOT_STRING)) { - msgs.add(m); - } - }); - PeelingUtils.bulkDelte(channel, msgs); - }); - ; + private long getDamage(Member member) { + return (long) Math.ceil(MAX_DAMAGE * Math.random()); } } diff --git a/src/main/java/net/uomc/mineshaft/SleepCommand.java b/src/main/java/net/uomc/mineshaft/SleepCommand.java new file mode 100644 index 0000000..ef97b8b --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/SleepCommand.java @@ -0,0 +1,128 @@ +package net.uomc.mineshaft; + +import java.awt.Color; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; + +import com.mouldycheerio.dbot.commands.CommandDetails; +import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand; +import com.mouldycheerio.dbot.commands.cooldowns.CooldownController; +import com.mouldycheerio.dbot.util.PeelingUtils; +import com.vdurmont.emoji.EmojiParser; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public class SleepCommand extends CooldownCommand { + + private static final Color COMMAND_COLOR = PeelingUtils.hex2Rgb("#b23028"); + private static final String COMMAND_TITLE = "Sleep"; + private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/thumb/Red_Bed_%28N%29.png/300px-Red_Bed_%28N%29.png"; + private static final double REDUCTION = 0.25; + private static final int MAX_ITEMS_DROP = 10; + private static final int MIN_ITEMS_DROP = 3; + private static final long MAX_DAMAGE = 30; + private static final long MIN_DAMAGE = 20; + private Mineshaft bot; + private static final String damageEmoji = EmojiParser.parseToUnicode(":fire:"); + + private long explodeCooldown; + + public SleepCommand(Mineshaft bot) { + super(bot); + setCommandDetails(CommandDetails.from("sleep", "go sleep", "sleep")); + this.bot = bot; + setCooldown(6l * 60l * 60l * 1000l); + setExplodeCooldown(60l * 1000l); + } + + @Override + public boolean trigger(MessageReceivedEvent e) { + + long beds = bot.getItem(e.getMember(), MineshaftItem.BED); + + if (beds < 1) { + bot.sendErrorMessage(e, ":x: You need to have at least **" + + bot.getItem(MineshaftItem.BED).prettyValue(1) + + "** to use this!"); + return false; + } + + if (bot.getPickaxes().isNether(e.getMember())) { + long damage = (long) Math.ceil((MAX_DAMAGE-MIN_DAMAGE)*Math.random() + MIN_DAMAGE); + String actionString = "Your bed exploded! " + PlayerHealths.getHPString(-damage) + "\n"; + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setColor(COMMAND_COLOR); + em.setThumbnail(COMMAND_IMAGE); + em.setDescription(actionString); + + bot.removeItem(e.getMember(), MineshaftItem.BED, 1); + + e.getChannel().sendMessageEmbeds(em.build()).queue(m -> { + + int itemsDrop = (int) (Math.random() * (MAX_ITEMS_DROP-MIN_ITEMS_DROP) + MIN_ITEMS_DROP); + Map award = new LinkedHashMap<>(); + for (int i = 0; i < itemsDrop; ++i) { + award = bot.sumItems(award, + bot.getMineCommand().getAwards(null, + bot.getMineCommand().getOre( + null, true))); + } + + long finalHP = bot.getHealths().damageAndDropIfKill(m, e.getMember(), damage, damageEmoji + " " + e.getMember().getAsMention() + " died to Intentional Game Design", award); + + if (finalHP > 0) { + bot.addItems(e.getMember(), award); + + MessageEmbed embed = DropUtil.getDropEmbed(bot, award, em.build()); + DropUtil.spawnDropEdit(bot, m, embed, award); + } + }); + + + getCooldownController().setNextTrigger(e.getMember(), this, System.currentTimeMillis() + getExplodeCooldown()); + return false; + } + + CooldownController cc = bot.getCommandController().getCooldownController(); + + cc.getTimers(e.getMember()).forEach(t -> { + long next = cc.getNextTime(e.getMember(), t); + long duration = next - System.currentTimeMillis(); + if (duration > 0) { + long newTimer = System.currentTimeMillis() + (long) Math.floor(duration * (1-REDUCTION)); + cc.setNextTrigger(e.getMember(), t, newTimer); + } + }); + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setColor(COMMAND_COLOR); + em.setThumbnail(COMMAND_IMAGE); + em.setDescription("You went to sleep\n\nCooldowns have been reduced by **" + ((int) Math.round(REDUCTION * 100)) + "%**:\n\n" + bot.getCooldownsString(e.getMember(), List.of("sleep"))); + + e.getMessage().replyEmbeds(em.build()).queue(); + return true; + } + + public Mineshaft getBot() { + return bot; + } + + public static String getDamageemoji() { + return damageEmoji; + } + + public long getExplodeCooldown() { + return explodeCooldown; + } + + public void setExplodeCooldown(long explodeCooldown) { + this.explodeCooldown = explodeCooldown; + } +} diff --git a/src/main/java/net/uomc/mineshaft/farm/CompostCommand.java b/src/main/java/net/uomc/mineshaft/farm/CompostCommand.java new file mode 100644 index 0000000..0da607d --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/farm/CompostCommand.java @@ -0,0 +1,136 @@ +package net.uomc.mineshaft.farm; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import com.mouldycheerio.dbot.commands.CommandDetails; +import com.mouldycheerio.dbot.commands.cooldowns.CooldownCommand; +import com.mouldycheerio.dbot.util.PeelingUtils; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.uomc.mineshaft.Mineshaft; +import net.uomc.mineshaft.MineshaftItem; + +public class CompostCommand extends CooldownCommand { + + private static final Color COMMAND_COLOUR = PeelingUtils.hex2Rgb("#603a19"); + private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/Composter_JE1.png"; + public static final int COMPOST_LEVEL = 7; + private static final String COMMAND_TITLE = "Composter"; + + Mineshaft bot; + List compostable; + + public CompostCommand(Mineshaft bot) { + super(bot); + setCommandDetails(CommandDetails.from("compost,compostor,composter", "access your compostor", "farm")); + + setCooldown(1000l * 30l); + + compostable = new ArrayList<>(); + compostable.add(MineshaftItem.CARROT); + compostable.add(MineshaftItem.FISH); + compostable.add(MineshaftItem.POTATO); + compostable.add(MineshaftItem.CANE); + + this.bot = bot; + } + + @Override + public boolean trigger(MessageReceivedEvent e) { + long composters = bot.getItem(e.getMember(), MineshaftItem.COMPOSTER); + String[] args = e.getMessage().getContentRaw().split("\\s+"); + + if (composters < 1) { + bot.sendErrorMessage(e, ":x: You need to have at least **" + + bot.getItem(MineshaftItem.COMPOSTER).prettyValue(1) + + "** to use this!"); + return false; + } + + if (args.length > 1) { + List argList = Arrays.asList(Arrays.copyOfRange(args, 1, args.length)); + + return compost(e, argList, composters); + } + + StringBuilder description = new StringBuilder(); + description.append(String.format("This is your compostor, level `%s`.\nUse `%scompost [item]` to compost your items\n\n", composters, bot.getPrefixManager().getPrefix(e.getGuild()), bot.getPrefixManager().getPrefix(e.getGuild()))); + + AtomicLong amount = new AtomicLong(); + compostable.forEach(i -> { + long q = bot.getItem(e.getMember(), i); + if (q < 1) { + return; + } + + long itemAmount = getMaxCompostable(e.getMember(), composters, i); + long bonemealAmount = (long) (itemAmount / COMPOST_LEVEL); + + description.append(String.format("%s->%s\n", bot.getItem(i).prettyValue(itemAmount), bot.getItem(MineshaftItem.BONEMEAL).prettyValue(bonemealAmount))); + }); + + if (amount.get() == 0) { + description.append("*You have nothing that can be composted*"); + } + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setDescription(description); + em.setColor(COMMAND_COLOUR); + em.setThumbnail(COMMAND_IMAGE); + + e.getMessage().replyEmbeds(em.build()).queue(); + return false; + } + + private boolean compost(MessageReceivedEvent e, List argList, long composters) { + Optional resourceName = argList.stream().findFirst(); + if (resourceName.isEmpty()) + return false; + + MineshaftItem item = MineshaftItem.valueOf(resourceName.get().toUpperCase()); + if (item == null) { + bot.sendErrorMessage(e, ":x: Please provide a valid item! `m!compost [item]`"); + return false; + } + + if (!compostable.contains(item)) { + bot.sendErrorMessage(e, ":x: This item cannot be composted"); + return false; + } + + long itemAmount = getMaxCompostable(e.getMember(), composters, item); + + // should never happen technically + if (itemAmount > bot.getItem(item).get(e.getMember())) { + bot.sendErrorMessage(e, ":x: You dont have enough " + bot.getItem(item).getSymbol()); + return true; + } + + long bonemealAmount = (long) (itemAmount / COMPOST_LEVEL); + + bot.removeItem(e.getMember(), item, itemAmount); + bot.addItem(e.getMember(), MineshaftItem.BONEMEAL, bonemealAmount); + + String content = String.format(":white_check_mark: You turned %s into %s", bot.getItem(item).prettyValue(itemAmount), bot.getItem(MineshaftItem.BONEMEAL).prettyValue(bonemealAmount)); + if (bot.getPickaxes().getFarmLevel(e.getMember()) < 1) { + content += "\nUse your bonemeal to expand your `m!farm`"; + } + bot.sendSuccessMessage(e, content); + + return true; + + } + + public long getMaxCompostable(Member m, long composters, MineshaftItem i) { + return Math.min(bot.getItem(m, i), composters*COMPOST_LEVEL); + } + +} diff --git a/src/main/java/net/uomc/mineshaft/farm/Crop.java b/src/main/java/net/uomc/mineshaft/farm/Crop.java new file mode 100644 index 0000000..7418fee --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/farm/Crop.java @@ -0,0 +1,21 @@ +package net.uomc.mineshaft.farm; + +import net.uomc.mineshaft.MineshaftItem; + +public enum Crop { + CANE(new String[]{ "<:cane_crop1:1426729699455012995>", "<:cane_crop2:1426729697999327242>", " <:cane_crop3:1426729696812601524>" }), + POTATO(new String[]{"<:potato_crop1:1426727443837878374>", "<:potato_crop2:1426727441816358983>", "<:potato_crop3:1426727440641953944>"}), + CARROT(new String[]{"<:carrot_crop1:1426727275511939174>", "<:carrot_crop2:1426727274270687382>", "<:carrot_crop3:1426727272471068888>"}) + ; + + private String[] emoji; + + Crop(String[] emoji) { + this.emoji = emoji; + MineshaftItem.valueOf(this.toString()); + } + + String[] getEmoji() { + return emoji; + } +} diff --git a/src/main/java/net/uomc/mineshaft/farm/Farm.java b/src/main/java/net/uomc/mineshaft/farm/Farm.java new file mode 100644 index 0000000..d990216 --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/farm/Farm.java @@ -0,0 +1,109 @@ +package net.uomc.mineshaft.farm; + +import java.io.File; +import java.sql.SQLException; + +import com.mouldycheerio.dbot.util.DatabaseUtils; + +import net.dv8tion.jda.api.entities.Member; +import net.uomc.mineshaft.Mineshaft; +import net.uomc.mineshaft.MineshaftItem; + +class Farm{ + + private MineshaftItem item; + + private String farmsDbPath; + private Mineshaft bot; + + public Farm(Mineshaft bot, MineshaftItem item) { + this.item = item; + this.farmsDbPath = (new File(bot.getDatadir(), "farms.db")).getPath(); + this.bot = bot; + + initDB(); + } + + private void initDB() { + try { + DatabaseUtils.createSimpleKVtable(farmsDbPath, item.toString() + "_access"); + } catch (SQLException e) { + } + try { + DatabaseUtils.createSimpleKVtable(farmsDbPath, item.toString() + "_planted"); + } catch (SQLException e) { + } + try { + DatabaseUtils.createSimpleKVtable(farmsDbPath, item.toString()); + } catch (SQLException e) { + } + } + + public void setPlanted(Member member, long value) { + try { + DatabaseUtils.putInKVtable(farmsDbPath, item.toString() + "_planted", member.getId() + ":" + member.getGuild().getId(), value); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public long getPlanted(Member member) { + try { + return DatabaseUtils.getInKVtable(farmsDbPath, item.toString() + "_planted", member.getId() + ":" + member.getGuild().getId()); + } catch (SQLException e) { + e.printStackTrace(); + } + return 0l; + } + + public void setQuantity(Member member, long value) { + try { + DatabaseUtils.putInKVtable(farmsDbPath, item.toString(), member.getId() + ":" + member.getGuild().getId(), value); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public long getQuantity(Member member) { + try { + return DatabaseUtils.getInKVtable(farmsDbPath, item.toString(), member.getId() + ":" + member.getGuild().getId()); + } catch (SQLException e) { + e.printStackTrace(); + } + return 0l; + } + + public void setNextUpdate(Member member, long value) { + try { + DatabaseUtils.putInKVtable(farmsDbPath, item.toString() + "_access", member.getId() + ":" + member.getGuild().getId(), value); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public long getNextUpdate(Member member) { + try { + return DatabaseUtils.getInKVtable(farmsDbPath, item.toString() + "_access", member.getId() + ":" + member.getGuild().getId()); + } catch (SQLException e) { + e.printStackTrace(); + } + return 0l; + } + + public void incrementPlanted(Member member, long d) { + long q = getPlanted(member); + + setPlanted(member, q + d); + } + + public long extractAll(Member member) { + long q = getQuantity(member); + + bot.addItem(member, item, q); + return q; + } + + public MineshaftItem getItem() { + return item; + } +} diff --git a/src/main/java/net/uomc/mineshaft/farm/FarmCommand.java b/src/main/java/net/uomc/mineshaft/farm/FarmCommand.java new file mode 100644 index 0000000..43b7576 --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/farm/FarmCommand.java @@ -0,0 +1,277 @@ +package net.uomc.mineshaft.farm; + +import static java.util.stream.Collectors.toMap; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.IntStream; + +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.entities.Member; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.uomc.mineshaft.Mineshaft; +import net.uomc.mineshaft.MineshaftItem; + +public class FarmCommand extends DetailedCommand { + + private static final Color COMMAND_COLOUR = PeelingUtils.hex2Rgb("#589430"); + public static final long TIME_BETWEEN_UPDATES = 1000l * 60l * 10l; + public static final int MAX_CROP_LEVEL = 2; + private static final String COMMAND_TITLE = "Farm"; + public static final long XP_PER_ITEM = 5000l; + + Mineshaft bot; + List farms; + + public FarmCommand(Mineshaft bot) { + setCommandDetails(CommandDetails.from("farm", "access your farm", "farm")); + this.bot = bot; + + farms = new ArrayList<>(); + farms.add(new Farm(bot, MineshaftItem.CANE)); + farms.add(new Farm(bot, MineshaftItem.CARROT)); + farms.add(new Farm(bot, MineshaftItem.POTATO)); + } + + @Override + public void execute(MessageReceivedEvent e, CustomBot b, String[] args) throws CommandFail { + List argList = Arrays.asList(args); + + if (bot.getPickaxes().isNether(e.getMember())) { + bot.sendErrorMessage(e, ":x: You cannot access your farm from the nether"); + return; + } + update(e.getMember()); + + if (args.length > 0 && "harvest".equalsIgnoreCase(args[0])) { + harvest(e, args); + return; + } + + if (args.length > 0 && "expand".equalsIgnoreCase(args[0])) { + expand(e, argList); + return; + } + + if (args.length > 0 && "plant".equalsIgnoreCase(args[0])) { + if (!plant(e, argList)) + bot.sendErrorMessage(e, String.format(":x: Usage `%sfarm plant [amount] [carrot|potato|cane]`", bot.getPrefixManager().getPrefix(e.getGuild()))); + return; + } + + long farmLevel = bot.getPickaxes().getFarmLevel(e.getMember()); + StringBuilder description = new StringBuilder(); + description.append(String.format("This is your farm.\nUse `%sfarm plant [carrot|potato|cane] [amount]` to plant\nUse `%sfarm harvest` to harvest your crops\nUse %s to expand your farm with `%sfarm expand`\n\n", + bot.getPrefixManager().getPrefix(e.getGuild()), + bot.getPrefixManager().getPrefix(e.getGuild()), + bot.getItem(MineshaftItem.BONEMEAL).getSymbol(), + bot.getPrefixManager().getPrefix(e.getGuild()))); + + description.append(String.format("Your farm current can currently hold `%s` crops\n\n", PeelingUtils.amountToString(farmLevel))); + + AtomicLong amount = new AtomicLong(); + farms.forEach(farm -> { + long level = Math.min(farm.getQuantity(e.getMember()), MAX_CROP_LEVEL); + long planted = farm.getPlanted(e.getMember()); + amount.addAndGet(planted); + + String emoji = getCropEmoji(farm.getItem(), (int) level); + + if (planted < 1) + return; + + IntStream.range(0, (int) Math.min(planted,12)).forEach(i -> { + description.append(emoji); + }); + + description.append(String.format("(%sx%s)\n", bot.getItem(farm.getItem()).getSymbol(), planted)); + }); + + if (amount.get() == 0) { + description.append("*You have no crops*"); + } + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setDescription(description); + em.setColor(COMMAND_COLOUR); + + if (farmLevel == 0) { + em.setFooter(EmojiParser.parseToUnicode(":bulb:") +"Use bonemeal to expand your farm with m!farm expand"); + } + + e.getMessage().replyEmbeds(em.build()).queue(); + } + + private boolean expand(MessageReceivedEvent e, List args) { + MineshaftItem item = MineshaftItem.BONEMEAL; + + long q = getItemAmountFromArgs(e, args, item); + if (q > bot.getItem(e.getMember(), item) || q < 1) { + bot.sendErrorMessage(e, ":x: You do not have enough " + bot.getItem(item).getSymbol()); + return false; + } + + bot.removeItem(e.getMember(), item, q); + long farmLevel = bot.getPickaxes().getFarmLevel(e.getMember()); + bot.getPickaxes().setFarmLevel(e.getMember(), farmLevel + q); + farmLevel = bot.getPickaxes().getFarmLevel(e.getMember()); + + bot.sendSuccessMessage(e, ":white_check_mark: You expanded your farm. Now it can hold " + farmLevel + " crops"); + return true; + } + + private long getItemAmountFromArgs(MessageReceivedEvent e, List args, MineshaftItem item) { + Optional all = args.stream().filter(s -> s.equals("all")).findFirst(); + if (all.isEmpty()) { + Optional quantity = args.stream().filter(s -> PeelingUtils.isLong(s)).map(s -> Long.parseLong(s)).findFirst(); + if (quantity.isPresent()) + return Math.abs(quantity.get()); + } + + return bot.getItem(item).get(e.getMember()); + } + + public String getCropEmoji(MineshaftItem item, int level) { + return Crop.valueOf(item.toString().toUpperCase()).getEmoji()[(int)Math.max(0, Math.min(2, level))]; + } + + public void update(Member m) { + farms.forEach(farm -> { + update(farm, m); + }); + } + + public void update(Farm farm, Member m) { + long next = farm.getNextUpdate(m); + long now = System.currentTimeMillis(); + if (next == 0) { + next = now; + } + + if (next > now) + return; + + long since = now - next; + long updates = since / TIME_BETWEEN_UPDATES; + + farm.setQuantity(m, Math.min(farm.getQuantity(m) + updates, MAX_CROP_LEVEL)); + farm.setNextUpdate(m, next + TIME_BETWEEN_UPDATES*updates); + } + + public void harvest(MessageReceivedEvent e, String[] args) { + Map award = new HashMap<>(); + AtomicLong total = new AtomicLong(); + farms.forEach( f -> { + long amount = harvest(f, e.getMember()); + total.addAndGet(amount); + if (amount > 0) + award.put(f.getItem(), amount); + }); + + String description = String.format("You harvested your crops: %s\nDon't forget to replant your farm!", bot.createItemList(award, "+%s", ",")); + + if (total.get() == 0) { + bot.sendErrorMessage(e, ":x: You have no crops!"); + return; + } + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setDescription(description); + em.setColor(COMMAND_COLOUR); + + e.getMessage().replyEmbeds(em.build()).queue(); + } + + public boolean plant(MessageReceivedEvent e, List args) { + if (args.size() > 3) + return false; + + Optional resourceName = args.stream().filter(s -> !s.equalsIgnoreCase("all") && !s.equalsIgnoreCase("plant") && !PeelingUtils.isLong(s)).findFirst(); + if (resourceName.isEmpty()) + return false; + + MineshaftItem item = MineshaftItem.valueOf(resourceName.get().toUpperCase()); + if (item == null) { + bot.sendErrorMessage(e, ":x: Please provide a valid item!"); + return true; + } + + long q = 0; + Optional all = args.stream().filter(s -> s.equals("all")).findFirst(); + if (all.isEmpty()) { + Optional quantity = args.stream().filter(s -> PeelingUtils.isLong(s)).map(s -> Long.parseLong(s)).findFirst(); + if (quantity.isEmpty()) + return false; + + q = Math.abs(quantity.get()); + } else { + q = bot.getItem(item).get(e.getMember()); + } + + + Optional farm = farms.stream().filter(f -> f.getItem() == item).findFirst(); + if (farm.isEmpty()) { + bot.sendErrorMessage(e, ":x: This item cannot be planted"); + return true; + } + + if (q > bot.getItem(item).get(e.getMember())) { + bot.sendErrorMessage(e, ":x: You dont have enough " + bot.getItem(item).getSymbol()); + return true; + } + + long planted = plant(farm.get(), e.getMember(), q); + if (planted > 0) + bot.sendSuccessMessage(e, String.format("You planted %s in your farm", bot.getItem(item).prettyValue(planted))); + else + bot.sendErrorMessage(e, ":x: You have no more space in your farm!\n Expand with `m!farm expand [amount]`"); + return true; + } + + public long harvest(Farm farm, Member member) { + long toGive = ((1+farm.getQuantity(member))) * farm.getPlanted(member); + farm.setQuantity(member, 0); + farm.setPlanted(member, 0); + + bot.addItem(member, farm.getItem(), toGive); + bot.addItem(member, MineshaftItem.XP, (long) (Math.random() * XP_PER_ITEM * toGive)); + return toGive; + } + + public long plant(Farm farm, Member member, long amount) { + long farmLevel = bot.getPickaxes().getFarmLevel(member); + long totalPlanted = getTotalPlanted(member); + if (totalPlanted + amount > farmLevel) { + amount = farmLevel - totalPlanted; + } + + if (amount <= 0) { + return 0; + } + + farm.incrementPlanted(member, amount); + farm.setQuantity(member, 0); + + bot.removeItem(member, farm.getItem(), amount); + return amount; + } + + public long getTotalPlanted(Member m) { + return farms.stream().mapToLong(f -> f.getPlanted(m)).sum(); + } +} diff --git a/src/main/java/net/uomc/mineshaft/farm/TradeCommand.java b/src/main/java/net/uomc/mineshaft/farm/TradeCommand.java new file mode 100644 index 0000000..f512afe --- /dev/null +++ b/src/main/java/net/uomc/mineshaft/farm/TradeCommand.java @@ -0,0 +1,239 @@ +package net.uomc.mineshaft.farm; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +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.MessageChannel; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.uomc.mineshaft.Mineshaft; +import net.uomc.mineshaft.MineshaftItem; +import net.uomc.mineshaft.resources.Resource; + +public class TradeCommand extends CooldownCommand { + + private static final UnicodeEmoji EXCLAIM = Emoji.fromUnicode(EmojiParser.parseToUnicode(":grey_exclamation:")); + private static final UnicodeEmoji YES = Emoji.fromUnicode(EmojiParser.parseToUnicode(":white_check_mark:")); + private static final UnicodeEmoji NO = Emoji.fromUnicode(EmojiParser.parseToUnicode(":x:")); + private static final Color COMMAND_COLOR = PeelingUtils.hex2Rgb("#375482"); + private static final String COMMAND_IMAGE = "https://minecraft.wiki/images/thumb/Wandering_Trader_JE1_BE1.png/270px-Wandering_Trader_JE1_BE1.png?62e9e"; + private static final String COMMAND_TITLE = "A wandering trader has spawned"; + private static final String ACCEPT_STRING = "accept"; + private static final double VALUE_MULTIPLIER = 1.1; + private static final double NEW_ITEM_ITEM_VALUE = 0.3; + private static final long TRADE_DURATION_SECONDS = 90; + + private static final double MAX_AMOUNT_PERCENT = 0.2; + + public static MineshaftItem[] traderItems = { + MineshaftItem.CARROT, + MineshaftItem.CANE, + MineshaftItem.POTATO, + MineshaftItem.BOOK, + MineshaftItem.LAPIS, + MineshaftItem.COAL, + }; + + public static MineshaftItem[] buyingItems = { + MineshaftItem.CARROT, + MineshaftItem.CANE, + MineshaftItem.POTATO + }; + + Mineshaft bot; + + public TradeCommand(Mineshaft bot) { + super(bot); + setCommandDetails(CommandDetails.from("trade,trader,wanderingtrader", "trade with the wandering trader", "trade")); + this.bot = bot; + + setCooldown(90l * 60l * 1000l); + } + + + @Override + public boolean trigger(MessageReceivedEvent e) { + + long villagers = bot.getItem(e.getMember(), MineshaftItem.VILLAGER); + + if (villagers < 1) { + bot.sendErrorMessage(e, ":x: You need to have at least **" + + bot.getItem(MineshaftItem.VILLAGER).prettyValue(1) + + "** to use this!"); + return false; + } + + String description = "The trader has a deal for you:\n\n"; + + MineshaftItem trade = newTrade(); + boolean inverse = isInverseTrade(e.getMember(), trade); + + MineshaftItem giveItem; + MineshaftItem getItem; + + if (inverse) { + giveItem = trade; + getItem = MineshaftItem.EMERALD; + } else { + getItem = trade; + giveItem = MineshaftItem.EMERALD; + } + + long q = getTradeQuantity(e.getMember(), getItem); + long p = getPrice(giveItem, getItem, q); + + long userGive = bot.getItem(e.getMember(), giveItem); + long userGet = bot.getItem(e.getMember(), getItem); + + description += String.format("**%s**->**%s**", bot.getItem(giveItem).prettyValue(p), bot.getItem(getItem).prettyValue(q)); + + description += String.format( + "\n\n:warning:Warning this offer expires in %s and you will need to wait `%s` before trading again", + PeelingUtils.formatTimeRelativeFromNow(TRADE_DURATION_SECONDS * 1000l), + PeelingUtils.formatTime(getCooldown(), false) + ); + + description += String.format("\n\nYou have **%s**", bot.getItem(giveItem).prettyValue(userGive)); + if (userGet > 0) { + description += String.format(", **%s**", bot.getItem(getItem).prettyValue(userGet)); + } + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setDescription(description); + em.setThumbnail(COMMAND_IMAGE); + em.setColor(COMMAND_COLOR); + + e.getMessage().replyEmbeds(em.build()).queue(m -> { + m.addReaction(YES).queue(m1 -> { + m.addReaction(NO).queue(m2 -> { + EventWaiter eventWaiter = new EventWaiter(); + e.getMessage().getJDA().addEventListener(eventWaiter); + + eventWaiter.waitForEvent(MessageReactionAddEvent.class, + e1 -> { + if (!e1.getMessageId().equals(m.getId())) + return false; + + if (!e1.getMember().getId().equals(e.getMember().getId())) + return false; + + + if (e1.getEmoji().asUnicode().getFormatted().equals(YES.getFormatted())) + return true; + + if (e1.getEmoji().asUnicode().getFormatted().equals(NO.getFormatted())) + return true; + + return false; + }, e1 -> { + if (e1.getEmoji().asUnicode().getFormatted().equals(YES.getFormatted())) { + acceptTrade(e.getMember(), giveItem, getItem, q, p, m); + } else { + m.delete().queue(); + } + }, TRADE_DURATION_SECONDS, TimeUnit.SECONDS, () -> { + m.delete().queue(); + }); + }); + }); + + }); + + e.getMessage().addReaction(EXCLAIM).queue(); + return true; + } + + private void acceptTrade(Member member, MineshaftItem giveItem, MineshaftItem getItem, long quantity, long price, Message message) { + String description = ""; + long userGive = bot.getItem(member, giveItem); + + if (userGive >= price) { + bot.removeItem(member, giveItem, price); + bot.addItem(member, getItem, quantity); + + description = String.format("You accepted the trade! +**%s**, -**%s**", bot.getItem(getItem).prettyValue(quantity), bot.getItem(giveItem).prettyValue(price)); + } else { + description = String.format("You don't have enough! You have **%s**", bot.getItem(giveItem).prettyValue(userGive)); + } + + description += "\nThe trader leaves, for now..."; + + EmbedBuilder em = new EmbedBuilder(); + em.setTitle(COMMAND_TITLE); + em.setDescription(description); + em.setThumbnail(COMMAND_IMAGE); + em.setColor(COMMAND_COLOR); + + message.clearReactions().queue(m -> { + message.editMessageEmbeds(em.build()).queue(m2 -> { + //m2.delete().queueAfter(10, TimeUnit.SECONDS); + }); + }); + } + + public void cleanup(MessageChannel channel, Message message) { + message.delete().queue(a -> {}, x -> {}); + + channel.getHistoryAfter(message, 100).queueAfter(5, TimeUnit.SECONDS, h -> { + ArrayList msgs = new ArrayList(); + h.getRetrievedHistory().forEach(m -> { + if (m.getContentRaw().equalsIgnoreCase(ACCEPT_STRING)) { + msgs.add(m); + } + }); + PeelingUtils.bulkDelte(channel, msgs); + }); + ; + } + + + public MineshaftItem newTrade() { + return traderItems[(int) (traderItems.length * Math.random())]; + } + + public long getTradeQuantity(Member m, MineshaftItem item) { + return (long) ((Math.random() * MAX_AMOUNT_PERCENT * bot.getItem(m, item)) + 1); + } + + public long getPrice(MineshaftItem giveItem, MineshaftItem getItem, long quantity) { + long giveValue = bot.getItem(giveItem).getValue(); + if (giveValue <= 0) { + giveValue = (long) (bot.getResourceManager().getTotalResources() * NEW_ITEM_ITEM_VALUE * Math.random()); + } + + long getValue = bot.getItem(getItem).getValue(); + if (getValue <= 0) { + getValue = (long) (bot.getResourceManager().getTotalResources() * NEW_ITEM_ITEM_VALUE * Math.random()); + } + + return (long) (quantity * getValue * VALUE_MULTIPLIER / giveValue) + 1; + } + + public boolean isInverseTrade(Member member, MineshaftItem item) { + boolean isBuying = false; + for (int i = 0; i < buyingItems.length; i++) { + if (buyingItems[i] == item) { + isBuying = true; + break; + } + } + if (isBuying) + return bot.getItem(member, item) > 0; + + return false; + } +} diff --git a/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java b/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java index da1bd83..5e892af 100644 --- a/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java +++ b/src/main/java/net/uomc/mineshaft/resources/ResourceManager.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Optional; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicLong; import org.json.JSONArray; import org.json.JSONObject; @@ -208,10 +209,13 @@ public class ResourceManager { setResource(member, resource, a + amount); } - public void addResources(Member member, Map resources) { + public long addResources(Member member, Map resources) { + AtomicLong totalAdded = new AtomicLong(0); resources.forEach((k, v) -> { addResource(member, k, v); + totalAdded.addAndGet(v); }); + return totalAdded.get(); } public String createResourceList(Map resources) { -- cgit v1.2.3