summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordavidovski <david@davidovski.xyz>2025-10-19 16:16:05 +0100
committerdavidovski <david@davidovski.xyz>2025-10-19 16:16:05 +0100
commitfff63aaea786a5f1c59bbf99c999a2aa7bb810e5 (patch)
treeb8c55be02d1e1f8575f5434a254d1593ede63432 /src
parentda9ece80133a33aff456d30854adc095b8f303ab (diff)
Add farm, sleep and trade
Diffstat (limited to 'src')
-rw-r--r--src/main/java/net/uomc/mineshaft/DropUtil.java136
-rw-r--r--src/main/java/net/uomc/mineshaft/FishCommand.java5
-rw-r--r--src/main/java/net/uomc/mineshaft/MineCommand.java8
-rw-r--r--src/main/java/net/uomc/mineshaft/Mineshaft.java95
-rw-r--r--src/main/java/net/uomc/mineshaft/Pickaxes.java2
-rw-r--r--src/main/java/net/uomc/mineshaft/PlayerHealths.java164
-rw-r--r--src/main/java/net/uomc/mineshaft/RobCommand.java97
-rw-r--r--src/main/java/net/uomc/mineshaft/SleepCommand.java128
-rw-r--r--src/main/java/net/uomc/mineshaft/farm/CompostCommand.java136
-rw-r--r--src/main/java/net/uomc/mineshaft/farm/Crop.java21
-rw-r--r--src/main/java/net/uomc/mineshaft/farm/Farm.java109
-rw-r--r--src/main/java/net/uomc/mineshaft/farm/FarmCommand.java277
-rw-r--r--src/main/java/net/uomc/mineshaft/farm/TradeCommand.java239
-rw-r--r--src/main/java/net/uomc/mineshaft/resources/ResourceManager.java6
14 files changed, 1311 insertions, 112 deletions
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<MineshaftItem, Long> getDeathDrops(Mineshaft bot, Member member) {
+ Map<MineshaftItem, Long> drop = new HashMap<>();
+
+ Map<MineshaftItem, Long> 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<MineshaftItem, Long> items) {
+ message.editMessageEmbeds(embed).setContent("").queue(m -> {
+ waitForPickUp(bot, embed, items, m);
+ });
+ }
+
+ private static void waitForPickUp(Mineshaft bot, MessageEmbed embed,
+ Map<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> 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<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/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<String> 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<String> 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<MineshaftItem, Long> items) {
- resourceManager.addResources(member, items.entrySet().stream()
+ public long addItems(Member member, Map<MineshaftItem, Long> 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<MineshaftItem, Long> items) {
- addItems(member, items.entrySet().stream().collect(Collectors.toMap(
+ public long removeItems(Member member, Map<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> items) {
+
+ Map<MineshaftItem, Long> 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<MineshaftItem, Long> 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<MineshaftItem, Long> 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<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);
- });
- ;
+ 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<MineshaftItem, Long> 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<MineshaftItem> 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<String> 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<String> argList, long composters) {
+ Optional<String> 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<Farm> 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<String> 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<String> 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<String> args, MineshaftItem item) {
+ Optional<String> all = args.stream().filter(s -> s.equals("all")).findFirst();
+ if (all.isEmpty()) {
+ Optional<Long> 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<MineshaftItem, Long> 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<String> args) {
+ if (args.size() > 3)
+ return false;
+
+ Optional<String> 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<String> all = args.stream().filter(s -> s.equals("all")).findFirst();
+ if (all.isEmpty()) {
+ Optional<Long> 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> 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<Message> msgs = new ArrayList<Message>();
+ 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<String, Long> resources) {
+ public long addResources(Member member, Map<String, Long> resources) {
+ AtomicLong totalAdded = new AtomicLong(0);
resources.forEach((k, v) -> {
addResource(member, k, v);
+ totalAdded.addAndGet(v);
});
+ return totalAdded.get();
}
public String createResourceList(Map<String, Long> resources) {