diff --git a/resources/rollLines.txt b/resources/rollLines.txt index e2c84de..fd3cab0 100644 --- a/resources/rollLines.txt +++ b/resources/rollLines.txt @@ -1,7 +1,5 @@ Are you feeling lucky? More doom to the DM! -Enjoy! -Here's some help! Are you sure this is a good idea? This is the moment of truth! Doom for the Game Master! diff --git a/src/main/java/commands/BleedCommand.java b/src/main/java/commands/BleedCommand.java index 5ccea11..32d45ac 100644 --- a/src/main/java/commands/BleedCommand.java +++ b/src/main/java/commands/BleedCommand.java @@ -10,13 +10,11 @@ import org.javacord.api.entity.message.embed.EmbedBuilder; import org.javacord.api.entity.permission.Role; import org.javacord.api.entity.user.User; -import players.PartyHandler; import sheets.PlotPointHandler; import sheets.SheetsHandler; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -26,13 +24,8 @@ public class BleedCommand implements CommandExecutor { @Command(aliases = {"~b", "~bleed"}, description = "Applies plot point bleed to a party", privateMessages = false, usage = "~b ") public void executeBleed(DiscordApi api, Message message, MessageAuthor author, TextChannel channel, Object[] parameters) { final List parties = new ArrayList<>(message.getMentionedRoles()); - // Add party roles mentioned by strings to parties - Arrays.stream(parameters) - .filter(partyName -> partyName instanceof String) - .forEach(partyName -> PartyHandler.getPartyByName((String) partyName).flatMap(party -> api.getRoleById(party.getRoleID())).ifPresent(parties::add)); // If there is a number, run the replenish function using the parties and that number as the amount to replenish bleedParty(parties.get(0), channel, author); - } /** diff --git a/src/main/java/commands/PlotPointCommand.java b/src/main/java/commands/PlotPointCommand.java index 0f7900b..11b2398 100644 --- a/src/main/java/commands/PlotPointCommand.java +++ b/src/main/java/commands/PlotPointCommand.java @@ -112,7 +112,7 @@ private void addPlotPointsAndSendSummary(List targets, int amount, TextCha // TODO Log errors final List>> plotPointUpdateFuture = targets.stream() .filter(user -> SheetsHandler.getPlotPoints(user).isPresent()) - .map(user -> PlotPointHandler.addPlotPointsToUser(amount, changes, new ArrayList<>(), user)) + .map(user -> PlotPointHandler.addPlotPointsToUser(user, amount, changes, new ArrayList<>())) .collect(Collectors.toList()); CompletableFuture.allOf(plotPointUpdateFuture.toArray(new CompletableFuture[]{})) .thenApply(unused -> channel.sendMessage(PlotPointHandler.generateEmbed(changes, channel, author))); diff --git a/src/main/java/commands/ReplenishCommand.java b/src/main/java/commands/ReplenishCommand.java index ef949da..8a83ccb 100644 --- a/src/main/java/commands/ReplenishCommand.java +++ b/src/main/java/commands/ReplenishCommand.java @@ -10,7 +10,6 @@ import org.javacord.api.entity.message.embed.EmbedBuilder; import org.javacord.api.entity.permission.Role; import org.javacord.api.entity.user.User; -import players.PartyHandler; import sheets.PlotPointHandler; import sheets.SheetsHandler; @@ -25,10 +24,6 @@ public class ReplenishCommand implements CommandExecutor { @Command(aliases = {"~replenish", "~sr", "~p addall"}, description = "Adds plot points to all party members of the specified party", privateMessages = false, usage = "~replenish [amount]") public void replenishPlotPoints(DiscordApi api, TextChannel channel, Message message, MessageAuthor author, Object[] parameters) { final List parties = new ArrayList<>(message.getMentionedRoles()); - // Add party roles mentioned by strings to parties - Arrays.stream(parameters) - .filter(partyName -> partyName instanceof String) - .forEach(partyName -> PartyHandler.getPartyByName((String) partyName).flatMap(party -> api.getRoleById(party.getRoleID())).ifPresent(parties::add)); // If there is a number, run the replenish function using the parties and that number as the amount to replenish Arrays.stream(parameters) .filter(o -> o instanceof Long).findFirst() @@ -50,7 +45,7 @@ private void replenishParties(Role party, int points, TextChannel channel, Messa List uneditablePlayers = new ArrayList<>(); final List>> replenishmentFutures = party.getUsers().stream() .filter(user -> SheetsHandler.getPlotPoints(user).isPresent()) - .map(user -> PlotPointHandler.addPlotPointsToUser(points, plotPointChanges, uneditablePlayers, user)) + .map(user -> PlotPointHandler.addPlotPointsToUser(user, points, plotPointChanges, uneditablePlayers)) .collect(Collectors.toList()); CompletableFuture.allOf(replenishmentFutures.toArray(new CompletableFuture[]{})) .thenAccept(unused -> sendReplenishmentResultEmbed(channel, author, plotPointChanges, uneditablePlayers)); diff --git a/src/main/java/commands/RollCommand.java b/src/main/java/commands/RollCommand.java index 2e47cfc..ab1373a 100644 --- a/src/main/java/commands/RollCommand.java +++ b/src/main/java/commands/RollCommand.java @@ -6,7 +6,6 @@ import dicerolling.DicePool; import dicerolling.DiceRoller; import dicerolling.PoolProcessor; -import dicerolling.SuccessCalculatorEmbed; import doom.DoomHandler; import logic.PlotPointEnhancementHelper; import org.apache.commons.lang3.tuple.Triple; @@ -16,116 +15,100 @@ import org.javacord.api.entity.message.MessageBuilder; import org.javacord.api.entity.message.embed.EmbedBuilder; import org.javacord.api.entity.user.User; +import org.javacord.api.event.message.reaction.ReactionAddEvent; +import roles.Storytellers; import sheets.PlotPointHandler; -import java.io.FileInputStream; -import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class RollCommand implements CommandExecutor { /** - * Rolls a pool of dice based on the input. After rolling, adds doom points to doom pool and makes appropriate changes the player's plot point count based on input options. If the DM is rolling, plot points they spend come from the doom point pool. + * Rolls a dice pool and sends the output to the channel the message was sent in + *

+ * Performs side effects based on the dice pool, sender, and if the roll is a reroll * - * @param author The author of the message - * @param message The message containing the command - * @param channel The channel the message was sent from + * @param channel The channel the command was sent from + * @param message The message for the command + * @param dicePool The dice pool to be rolled + * @param isReroll Whether the roll is a reroll */ - @Command(aliases = {"~r", "~roll"}, description = "A command that allows you to roll dice\n\tdie: A string representing a die. Some die examples are d4, pd12, 3d12, kd12, +3.\n\tskill: The value of a cell from a character's spreadsheet with no spaces and all lowercase.", privateMessages = false, usage = "~r [-fsu=x|-fsd=x|-maxf=x|-diff=|-k=x|-pdisc=x|-enh=true/false|-opp=true/false|-nd=pd/d/kd|-minf=x] die|skill [die|skill ...]") - public void rollDice(MessageAuthor author, Message message, TextChannel channel) { - String messageContent = message.getContent(); + private static void rollPoolAndSend(TextChannel channel, Message message, DicePool dicePool, boolean isReroll) { + DiceRoller diceRoller = new DiceRoller(dicePool); + final CompletableFuture sentMessageFuture = new MessageBuilder() + .setEmbed(diceRoller.generateResults(message.getAuthor())) + .send(channel); + sentMessageFuture.thenAcceptAsync(sentMessage -> { + if (!isReroll) { + handlePlotPointChanges(message, dicePool.getPlotPointsSpent(), dicePool.getPlotPointDiscount()); + } + if (!Storytellers.isMessageAuthorStoryteller(message.getAuthor()) && dicePool.areOpportunitiesEnabled()) { + handleOpportunities(message.getAuthor(), sentMessage, channel, dicePool.areOpportunitiesEnabled(), diceRoller.getDoom()); + } + if (dicePool.isEnhancementEnabled()) { + PlotPointEnhancementHelper.addPlotPointEnhancementEmojis(sentMessage); + queueReactionRemoval(sentMessage); + } + if (!isReroll) { + attachRerollReaction(message, dicePool, diceRoller, sentMessage, channel); + } + }); + } - //Variables containing roll information - final PoolProcessor poolProcessor = new PoolProcessor(messageContent, author); - if (poolProcessor.getErrorEmbed() != null) { - new MessageBuilder().setEmbed(poolProcessor.getErrorEmbed()).send(channel); + /** + * Re-rolls the previous roll. Plot points spent by the player are not spent again, plot and doom points from opportunites are reverted before the re-roll. + * + * @param channel The channel the reroll is in + * @param message The message that started the reroll + * @param dicePool The dice pool to be re-rolled + * @param doom The number of doom points generated by the previous roll + */ + private static void reroll(TextChannel channel, Message message, DicePool dicePool, int doom) { + // Revert opportunities and plot point spending + final EmbedBuilder doomEmbed = DoomHandler.addDoom(doom * -1); + final int changeInPlotPoints = doom * -1 + dicePool.getPlotPointsSpent() - dicePool.getPlotPointDiscount(); + if (changeInPlotPoints != 0) { + final ArrayList> plotPointChanges = new ArrayList<>(); + PlotPointHandler.addPlotPointsToUser(message.getUserAuthor().get(), changeInPlotPoints, plotPointChanges, new ArrayList<>()).thenAccept(integer -> channel.sendMessage(PlotPointHandler.generateEmbed(plotPointChanges, channel, message.getAuthor()))).join(); } - else { - final DicePool dicePool = poolProcessor.getDicePool(); - - DiceRoller diceRoller = new DiceRoller(dicePool); - final CompletableFuture sentMessageFuture = new MessageBuilder() - .setEmbed(diceRoller.generateResults(message.getAuthor())) - .send(channel); - sentMessageFuture.thenAcceptAsync(sentMessage -> { - handleMessageSideEffects(message, dicePool, diceRoller, sentMessage); - if (dicePool.enableEnhancements()) { - sentMessage.getApi().getThreadPool().getScheduler().schedule(() -> { - PlotPointEnhancementHelper.removeEnhancementEmojis(sentMessage); - final EmbedBuilder embedWithoutFooter = sentMessage.getEmbeds().get(0).toBuilder().setFooter(""); - sentMessage.edit(embedWithoutFooter); - }, 60, TimeUnit.SECONDS); - } - }); - + if (doom * -1 != 0) { + channel.sendMessage(doomEmbed).join(); } + rollPoolAndSend(channel, message, dicePool, true); } + public static void rollPoolAndSend(TextChannel channel, Message message, DicePool dicePool) { + rollPoolAndSend(channel, message, dicePool, false); + } /** - * Handles the side effects after rolling a pool of dice such as modifying the doom pool and plot point pool + * Deletes the reactions from a message after 60 seconds * - * @param userMessage The message that was sent by a Discord user - * @param dicePool The dice pool that was rolled - * @param diceRoller The DiceRoller object with information about the result of the dice rolled - * @param sentMessage The message with the embed containing the roll result + * @param sentMessage The message containing the embed for the roll */ - public static void handleMessageSideEffects(Message userMessage, DicePool dicePool, DiceRoller diceRoller, Message sentMessage) { - Properties prop = new Properties(); - try (FileInputStream stream = new FileInputStream("resources/bot.properties")) { - prop.load(stream); - } catch (IOException e) { - e.printStackTrace(); - } - final int plotPointsSpent = dicePool.getPlotPointsSpent() - dicePool.getPlotPointDiscount(); - MessageAuthor author = userMessage.getAuthor(); - TextChannel channel = userMessage.getChannel(); - //DMs use doom points as plot points and 1s do not increase the doom pool - if (author.getIdAsString().equals(prop.getProperty("gameMaster", ""))) { - if (plotPointsSpent != 0) { - EmbedBuilder doomEmbed = DoomHandler.addDoom(plotPointsSpent * -1); - channel.sendMessage(doomEmbed); - } - } - //Players have to spend plot points and gain doom points on opportunities - else { - handlePlayerRoll(sentMessage, author, channel, dicePool.enableOpportunities(), plotPointsSpent, diceRoller.getDoom()); - } - if (!dicePool.getDifficulty().equals("")) { - channel.sendMessage(SuccessCalculatorEmbed.generateDifficultyEmbed(dicePool.getDifficulty(), diceRoller.getTotal(), author)); - } - if (dicePool.enableEnhancements()) { - PlotPointEnhancementHelper.addPlotPointEnhancementEmojis(sentMessage); - } + private static void queueReactionRemoval(Message sentMessage) { + sentMessage.getApi().getThreadPool().getScheduler().schedule(() -> PlotPointEnhancementHelper.removeEnhancementEmojis(sentMessage), 60, TimeUnit.SECONDS); } /** - * Handles the modification of a player's plot points and the doom pool after the roll + * Adds plot points and doom points when rolling a 1 * - * @param sentMessage The message for the result of the roll - * @param author The player that made the roll - * @param channel The channel the roll was made in - * @param enableOpportunities If opportunities are enabled - * @param plotPointsSpent The number of plot points spent in the roll - * @param doomGenerated How many 1s were rolled in that roll + * @param author The author that made the roll + * @param rollEmbedMessage The message with the embed for the roll + * @param channel The channel the roll was made in + * @param areOpportunitiesEnabled Whether opportunities are enabled + * @param doomGenerated The amount of doom generated */ - private static void handlePlayerRoll(Message sentMessage, MessageAuthor author, TextChannel channel, boolean enableOpportunities, int plotPointsSpent, int doomGenerated) { - if (plotPointsSpent != 0 && author.asUser().isPresent()) { - ArrayList> plotPointChanges = new ArrayList<>(); - PlotPointHandler.addPlotPointsToUser(plotPointsSpent * -1, plotPointChanges, new ArrayList<>(), author.asUser().get()) - .thenAccept(integer -> channel.sendMessage(PlotPointHandler.generateEmbed(plotPointChanges, channel, author) - .setTitle(MessageFormat.format("Using {0} plot points!", plotPointsSpent)))).join(); - } + private static void handleOpportunities(MessageAuthor author, Message rollEmbedMessage, TextChannel channel, boolean areOpportunitiesEnabled, int doomGenerated) { // Send embed for plot points and doom if there's an opportunity - if (enableOpportunities && doomGenerated >= 1) { - sentMessage.addReaction(EmojiParser.parseToUnicode(":eight_pointed_black_star:")); + if (areOpportunitiesEnabled && doomGenerated >= 1) { + rollEmbedMessage.addReaction(EmojiParser.parseToUnicode(":eight_pointed_black_star:")); EmbedBuilder doomEmbed = DoomHandler.addDoom(doomGenerated); + ArrayList> plotPointChanges = new ArrayList<>(); if (author.asUser().isPresent()) { - ArrayList> plotPointChanges = new ArrayList<>(); - PlotPointHandler.addPlotPointsToUser(1, plotPointChanges, new ArrayList<>(), author.asUser().get()) + PlotPointHandler.addPlotPointsToUser(author.asUser().get(), 1, plotPointChanges, new ArrayList<>()) .thenAccept(integer -> channel.sendMessage(PlotPointHandler.generateEmbed(plotPointChanges, channel, author).setTitle("An opportunity!"))) .join(); doomEmbed.setFooter(MessageFormat.format("Generated by {0}!", PlotPointHandler.getUsernameInChannel(author.asUser().get(), channel))); @@ -134,4 +117,88 @@ private static void handlePlayerRoll(Message sentMessage, MessageAuthor author, } } + /** + * Sends an embed for spending plot points on a roll + * + * @param message The message of the command + * @param pointsSpent The number of plot points spent + * @param discount The discount on the roll + */ + private static void handlePlotPointChanges(Message message, int pointsSpent, int discount) { + final int plotPointsSpent = pointsSpent - discount; + MessageAuthor author = message.getAuthor(); + TextChannel channel = message.getChannel(); + if (plotPointsSpent != 0) { + if (Storytellers.isMessageAuthorStoryteller(author)) { + //DMs use doom points as plot points and 1s do not increase the doom pool + EmbedBuilder doomEmbed = DoomHandler.addDoom(plotPointsSpent * -1); + channel.sendMessage(doomEmbed.setTitle(MessageFormat.format("Using {0} doom points!", plotPointsSpent))); + } + else if (author.asUser().isPresent()) { + //Players have to spend plot points and gain doom points on opportunities + ArrayList> plotPointChanges = new ArrayList<>(); + PlotPointHandler.addPlotPointsToUser(author.asUser().get(), plotPointsSpent * -1, plotPointChanges, new ArrayList<>()) + .thenAccept(integer -> channel.sendMessage(PlotPointHandler.generateEmbed(plotPointChanges, channel, author) + .setTitle(MessageFormat.format("Using {0} plot points!", plotPointsSpent)))).join(); + } + } + } + + /** + * Attach the reroll reaction and attach a listener that lasts for 60 seconds (same as the other reacts) + * + * @param userMessage The message containing the roll command + * @param dicePool The dice pool for rolling + * @param diceRoller The dice roller object containing the result of the roll + * @param sentMessage The message that contains the embed with the roll result + * @param channel The channel the message was sent from + */ + public static void attachRerollReaction(Message userMessage, DicePool dicePool, DiceRoller diceRoller, Message sentMessage, TextChannel channel) { + sentMessage.addReaction(EmojiParser.parseToUnicode(":repeat:")).thenAccept(unused -> sentMessage.addReactionAddListener(event -> { + onRerollReact(userMessage, dicePool, diceRoller, sentMessage, channel, event); + }).removeAfter(60, TimeUnit.SECONDS)); + } + + /** + * When a user reacts to the repeat reaction, + * + * @param userMessage The message the user sent to roll dice + * @param dicePool The dice pool for the roll + * @param diceRoller The dice roller object containing the results of the roll + * @param sentMessage The message with an embed containing result of the roll + * @param channel The channel the message was sent in + * @param event The reaction event + */ + private static void onRerollReact(Message userMessage, DicePool dicePool, DiceRoller diceRoller, Message sentMessage, TextChannel channel, ReactionAddEvent event) { + if (event.getUser().map(user -> !user.isYourself()).orElse(false) && event.getReaction().map(reaction -> reaction.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":repeat:"))).orElse(false)) { + PlotPointEnhancementHelper.removeEnhancementEmojis(sentMessage) + .thenAccept(reactionsRemoved -> { + reroll(channel, userMessage, dicePool, diceRoller.getDoom()); + event.addReactionsToMessage(EmojiParser.parseToUnicode(":bulb:")); + }); + } + } + + /** + * Rolls a pool of dice based on the input. After rolling, adds doom points to doom pool and makes appropriate changes the player's plot point count based on input options. If the DM is rolling, plot points they spend come from the doom point pool. + * + * @param author The author of the message + * @param message The message containing the command + * @param channel The channel the message was sent from + */ + @Command(aliases = {"~r", "~roll"}, description = "A command that allows you to roll dice\n\tdie: A string representing a die. Some die examples are d4, pd12, 3d12, kd12, +3.\n\tskill: The value of a cell from a character's spreadsheet with no spaces and all lowercase.", privateMessages = false, usage = "~r [-fsu=x|-fsd=x|-maxf=x|-diff=|-k=x|-pdisc=x|-enh=true/false|-opp=true/false|-nd=pd/d/kd|-minf=x] die|skill [die|skill ...]") + public void onRollCommand(MessageAuthor author, Message message, TextChannel channel) { + String messageContent = message.getContent(); + + //Variables containing roll information + final PoolProcessor poolProcessor = new PoolProcessor(author, messageContent); + if (poolProcessor.getErrorEmbed() != null) { + new MessageBuilder().setEmbed(poolProcessor.getErrorEmbed()).send(channel); + } + else { + final DicePool dicePool = poolProcessor.getDicePool(); + rollPoolAndSend(channel, message, dicePool); + } + } + } diff --git a/src/main/java/commands/TestRollCommand.java b/src/main/java/commands/TestRollCommand.java index 4410b00..2edbfbd 100644 --- a/src/main/java/commands/TestRollCommand.java +++ b/src/main/java/commands/TestRollCommand.java @@ -3,45 +3,25 @@ import de.btobastian.sdcf4j.Command; import de.btobastian.sdcf4j.CommandExecutor; import dicerolling.DicePool; -import dicerolling.DiceRoller; import dicerolling.PoolProcessor; -import logic.PlotPointEnhancementHelper; import org.javacord.api.entity.channel.TextChannel; import org.javacord.api.entity.message.Message; import org.javacord.api.entity.message.MessageAuthor; import org.javacord.api.entity.message.MessageBuilder; -import org.javacord.api.entity.message.embed.EmbedBuilder; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; public class TestRollCommand implements CommandExecutor { @Command(aliases = {"~t", "~test"}, description = "A command that allows you to roll dice without automatically subtracting plot points or doom points.\n\tdie: A string representing a die. Some die examples are d4, pd12, 3d12, kd12, +3.", privateMessages = false, usage = "~t die|skill [die|skill ...]") public void onCommand(Message message, MessageAuthor author, TextChannel channel) { String messageContent = message.getContent(); + final PoolProcessor poolProcessor = new PoolProcessor(author, messageContent); + poolProcessor.getDicePool().setOpportunitiesEnabled(false); - //Variables containing roll information, disable opportunities and plot point costs - final PoolProcessor poolProcessor = new PoolProcessor(messageContent, author); - poolProcessor.getDicePool().setOpportunities(false); if (poolProcessor.getErrorEmbed() != null) { new MessageBuilder().setEmbed(poolProcessor.getErrorEmbed()).send(channel); } else { final DicePool dicePool = poolProcessor.getDicePool(); - DiceRoller diceRoller = new DiceRoller(dicePool); - final CompletableFuture sentMessageFuture = new MessageBuilder() - .setEmbed(diceRoller.generateResults(message.getAuthor())) - .send(channel); - sentMessageFuture.thenAcceptAsync(sentMessage -> { - RollCommand.handleMessageSideEffects(message, dicePool, diceRoller, sentMessage); - if (dicePool.enableEnhancements()) { - sentMessage.getApi().getThreadPool().getScheduler().schedule(() -> { - PlotPointEnhancementHelper.removeEnhancementEmojis(sentMessage); - final EmbedBuilder embedWithoutFooter = sentMessage.getEmbeds().get(0).toBuilder().setFooter(""); - sentMessage.edit(embedWithoutFooter); - }, 60, TimeUnit.SECONDS); - } - }); + RollCommand.rollPoolAndSend(channel, message, dicePool); } } } diff --git a/src/main/java/dicerolling/DicePool.java b/src/main/java/dicerolling/DicePool.java index df2df1a..b113683 100644 --- a/src/main/java/dicerolling/DicePool.java +++ b/src/main/java/dicerolling/DicePool.java @@ -12,10 +12,10 @@ public class DicePool { private List chaosDice = new ArrayList<>(); //Configs private int keepHowMany = 2; - private boolean enableEnhancementEmojis = true; + private boolean isEnhancementEnabled = true; private String difficulty = ""; private int plotPointDiscount = 0; - private boolean enableOpportunities = true; + private boolean areOpportunitiesEnabled = true; private int minFacets = 0; public List getRegularDice() { @@ -51,8 +51,8 @@ public DicePool setNumberOfKeptDice(int num) { return this; } - public boolean enableEnhancements() { - return enableEnhancementEmojis; + public boolean isEnhancementEnabled() { + return isEnhancementEnabled; } public String getDifficulty() { @@ -72,8 +72,8 @@ public void setPlotPointDiscount(int num) { plotPointDiscount = num; } - public boolean enableOpportunities() { - return enableOpportunities; + public boolean areOpportunitiesEnabled() { + return areOpportunitiesEnabled; } public DicePool addChaosDie(int dice) { @@ -98,8 +98,8 @@ public DicePool addKeptDice(int dice) { return this; } - public DicePool setOpportunities(boolean enable) { - enableOpportunities = enable; + public DicePool setOpportunitiesEnabled(boolean enable) { + areOpportunitiesEnabled = enable; return this; } @@ -129,7 +129,7 @@ public DicePool addFlatBonus(int bonus) { } public DicePool enableEnhancement(boolean enable) { - enableEnhancementEmojis = enable; + isEnhancementEnabled = enable; return this; } diff --git a/src/main/java/dicerolling/DiceRoller.java b/src/main/java/dicerolling/DiceRoller.java index 3f6e559..0df537b 100644 --- a/src/main/java/dicerolling/DiceRoller.java +++ b/src/main/java/dicerolling/DiceRoller.java @@ -20,6 +20,7 @@ public class DiceRoller { public DiceRoller(DicePool dicePool) { this.dicePool = dicePool; rollResult = new RollResult(dicePool.getNumberOfKeptDice()); + // Disable enhancement by default if there is plot dice in the pool if (!dicePool.getPlotDice().isEmpty()) { dicePool.enableEnhancement(false); } @@ -57,7 +58,7 @@ public EmbedBuilder generateResults(MessageAuthor author) { .addField("Dropped", formatRegularDiceResults(rollResult.getAllDroppedDice(), false), true) .addField("Total", String.valueOf(rollResult.getTotal()), true) .addField("Tier Hit", rollResult.getTierHit(), true); - if (dicePool.enableEnhancements()) { + if (dicePool.isEnhancementEnabled()) { rollResultEmbed.setFooter("Enhance this roll with plot points in the next 60 seconds by clicking on the reactions below!"); } return rollResultEmbed; diff --git a/src/main/java/dicerolling/PoolProcessor.java b/src/main/java/dicerolling/PoolProcessor.java index 0f4898b..e9a088a 100644 --- a/src/main/java/dicerolling/PoolProcessor.java +++ b/src/main/java/dicerolling/PoolProcessor.java @@ -4,6 +4,7 @@ import org.javacord.api.entity.message.MessageAuthor; import org.javacord.api.entity.message.embed.EmbedBuilder; import org.javacord.api.entity.user.User; +import roles.Storytellers; import sheets.SheetsHandler; import java.io.FileInputStream; @@ -19,7 +20,7 @@ public class PoolProcessor { private Map skillMap; private EmbedBuilder errorEmbed; - public PoolProcessor(String command, MessageAuthor author) { + public PoolProcessor(MessageAuthor author, String command) { this.command = command; this.author = author; preprocess(); @@ -69,7 +70,7 @@ else if (param.matches("-enh=(true|false)")) { dicePool.enableEnhancement(Boolean.parseBoolean(param.substring(9))); } else if (param.matches("-opp=(true|false)")) { - dicePool.setOpportunities(Boolean.parseBoolean(param.substring(5))); + dicePool.setOpportunitiesEnabled(Boolean.parseBoolean(param.substring(5))); } else if (param.matches("-nd=(d|kd|pd|cd)")) { nextDiceType = param.substring(4); @@ -114,6 +115,7 @@ else if (param.chars().allMatch(Character::isLetter)) { /** * Checks whether a player should be allowed to enhance their roll or not + * TODO Change to check if there's a plot die * * @param author The player who is rolling * @return true if the player is on the override list and enhancements are disabled or if the player is not on the override list and enhancements are enabled, false if enhancement is disabled and the player is not on the override list or if enhancements are enabled and the player is on the override list @@ -130,9 +132,8 @@ private boolean getDefaultEnhancementOption(MessageAuthor author) { .map(Optional::get) .collect(Collectors.toCollection(ArrayList::new)); - //Gamemasters can always enhance - Optional gameMasterOptional = author.getApi().getCachedUserById(prop.getProperty("gameMaster")); - if (gameMasterOptional.isPresent() && author.asUser().isPresent() && gameMasterOptional.get().equals(author.asUser().get())) { + //Storytellers can always enhance + if (Storytellers.isMessageAuthorStoryteller(author)) { return true; } return (author.asUser().isPresent() && overrideUsers.contains(author.asUser().get())) != defaultOption.equals(EnhancementToggleCommand.ON); diff --git a/src/main/java/listeners/PlotPointEnhancementListener.java b/src/main/java/listeners/PlotPointEnhancementListener.java index 13396e9..688d4e4 100644 --- a/src/main/java/listeners/PlotPointEnhancementListener.java +++ b/src/main/java/listeners/PlotPointEnhancementListener.java @@ -11,13 +11,11 @@ import org.javacord.api.entity.user.User; import org.javacord.api.event.message.reaction.ReactionAddEvent; import org.javacord.api.listener.message.reaction.ReactionAddListener; +import roles.Storytellers; import sheets.SheetsHandler; -import java.io.FileInputStream; -import java.io.IOException; import java.util.Map; import java.util.Optional; -import java.util.Properties; /** * Listener that listens for a user using plot point enhancement @@ -63,7 +61,7 @@ private void enhanceRoll(ReactionAddEvent event, Reaction reaction, Message mess Emoji emoji = reaction.getEmoji(); Optional toAdd = getAddAmount(emoji); toAdd.ifPresent(count -> { - if (getGameMaster().equals(event.getUserIdAsString())) { + if (Storytellers.isMessageAuthorStoryteller(message.getAuthor())) { event.requestUser().thenAccept(user -> sendDoomPointEnhancementMessage(user, message.getChannel(), rollVal, count)); } else { @@ -73,16 +71,6 @@ private void enhanceRoll(ReactionAddEvent event, Reaction reaction, Message mess } - private String getGameMaster() { - Properties props = new Properties(); - try { - props.load(new FileInputStream("resources/bot.properties")); - } catch (IOException e) { - e.printStackTrace(); - } - return props.getProperty("gameMaster"); - } - /** * Subtracts plot point from player to enhance a roll * diff --git a/src/main/java/logic/PlotPointEnhancementHelper.java b/src/main/java/logic/PlotPointEnhancementHelper.java index 8d71d43..55adac7 100644 --- a/src/main/java/logic/PlotPointEnhancementHelper.java +++ b/src/main/java/logic/PlotPointEnhancementHelper.java @@ -3,6 +3,7 @@ import com.vdurmont.emoji.EmojiParser; import org.javacord.api.entity.emoji.Emoji; import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.embed.EmbedBuilder; import java.util.ArrayList; import java.util.SortedMap; @@ -42,11 +43,10 @@ public static CompletableFuture removeEnhancementEmojis(Message message) { .map(message::removeReactionsByEmoji) .collect(Collectors.toCollection(ArrayList::new)); - // TODO Make this less hacky //Remove Cancel Emoji - CompletableFuture cancelEmojiFuture = message.removeReactionsByEmoji("\uD83C\uDDFD"); - completableFutures.add(cancelEmojiFuture); - + completableFutures.add(message.removeReactionsByEmoji("\uD83C\uDDFD", EmojiParser.parseToUnicode(":repeat:"))); + final EmbedBuilder embedWithoutFooter = message.getEmbeds().get(0).toBuilder().setFooter(""); + message.edit(embedWithoutFooter); return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])); } diff --git a/src/main/java/players/Party.java b/src/main/java/players/Party.java deleted file mode 100644 index 4e187d4..0000000 --- a/src/main/java/players/Party.java +++ /dev/null @@ -1,62 +0,0 @@ -package players; - -import com.google.gson.annotations.Expose; -import com.google.gson.annotations.SerializedName; - -import java.util.List; - -public class Party { - @SerializedName("name") - @Expose - private String name; - - @SerializedName("members") - @Expose - private List members; - - @SerializedName("channel") - @Expose - private long channel; - - @SerializedName("roleID") - @Expose - private long roleID; - - public Party(String name, List members) { - super(); - this.name = name; - this.members = members; - } - - public long getRoleID() { - return roleID; - } - - public void setRoleID(long roleID) { - this.roleID = roleID; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getMembers() { - return members; - } - - public void setMembers(List members) { - this.members = members; - } - - public long getChannel() { - return channel; - } - - public void setChannel(int channel) { - this.channel = channel; - } -} diff --git a/src/main/java/players/PartyHandler.java b/src/main/java/players/PartyHandler.java deleted file mode 100644 index 9869aa2..0000000 --- a/src/main/java/players/PartyHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package players; - -import com.google.gson.Gson; -import org.javacord.api.DiscordApi; -import org.javacord.api.entity.user.User; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -public class PartyHandler { - private static final PartyHandler instance = new PartyHandler(); - private PartyConfig parties; - - private PartyHandler() { - try { - parties = new Gson().fromJson(new BufferedReader(new FileReader("resources/parties.json")), PartyConfig.class); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } - - /** - * Gets a list of users in a party - * - * @param partyName The name of the party - * @param api The Discord API - * @return A list of User objects that represents the characters in a party - */ - public static List> getPartyMembers(String partyName, DiscordApi api) { - List> completableFutures; - completableFutures = instance.parties.getParties().stream() - .filter(party -> party.getName().equals(partyName)) - .findFirst() - .map(party -> party.getMembers().stream().mapToLong(PartyMember::getDiscordId).mapToObj(api::getUserById).collect(Collectors.toList())) - .orElse(new ArrayList<>()); - return completableFutures; - } - - public static List getParties() { - return instance.parties.getParties(); - } - - public static Optional getPartyByName(String name) { - return getParties().stream().filter(party -> party.getName().equals(name)).findFirst(); - } - - static class PartyConfig { - private List parties; - - public List getParties() { - return parties; - } - - public void setParties(List parties) { - this.parties = parties; - } - - } -} diff --git a/src/main/java/players/PartyMember.java b/src/main/java/roles/Player.java similarity index 54% rename from src/main/java/players/PartyMember.java rename to src/main/java/roles/Player.java index 7604ba6..4315d7e 100644 --- a/src/main/java/players/PartyMember.java +++ b/src/main/java/roles/Player.java @@ -1,18 +1,18 @@ -package players; +package roles; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class PartyMember { +public class Player { @SerializedName("discord_id") @Expose - private long discordId; + private final long discordId; @SerializedName("sheet_id") @Expose - private String sheetId; + private final String sheetId; - public PartyMember(Integer discordId, String sheetId) { + public Player(Integer discordId, String sheetId) { this.discordId = discordId; this.sheetId = sheetId; } @@ -21,15 +21,8 @@ public long getDiscordId() { return discordId; } - public void setDiscordId(Integer discordId) { - this.discordId = discordId; - } - public String getSheetId() { return sheetId; } - public void setSheetId(String sheetId) { - this.sheetId = sheetId; - } } diff --git a/src/main/java/roles/PlayerHandler.java b/src/main/java/roles/PlayerHandler.java new file mode 100644 index 0000000..cbbbe5b --- /dev/null +++ b/src/main/java/roles/PlayerHandler.java @@ -0,0 +1,32 @@ +package roles; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.List; + +public class PlayerHandler { + private static final PlayerHandler instance = new PlayerHandler(); + private List players; + + private PlayerHandler() { + try { + players = new Gson().fromJson(new BufferedReader(new FileReader("resources/players.json")), TypeToken.getParameterized(ArrayList.class, Player.class).getType()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + players = new ArrayList<>(); + } + } + + public static PlayerHandler getInstance() { + return instance; + } + + public List getPlayers() { + return players; + } +} diff --git a/src/main/java/roles/Storytellers.java b/src/main/java/roles/Storytellers.java new file mode 100644 index 0000000..6542420 --- /dev/null +++ b/src/main/java/roles/Storytellers.java @@ -0,0 +1,61 @@ +package roles; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.message.MessageAuthor; +import org.javacord.api.entity.permission.Role; +import org.javacord.api.entity.user.User; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class Storytellers { + private static final Storytellers instance = new Storytellers(); + private List storytellerRoles; + + private Storytellers() { + try { + storytellerRoles = new Gson().fromJson(new BufferedReader(new FileReader("resources/storytellers.json")), TypeToken.getParameterized(ArrayList.class, Long.class).getType()); + } catch (FileNotFoundException e) { + storytellerRoles = new ArrayList<>(); + } + } + + /** + * Gets all of the storyteller role + * + * @param api The Discord API + * @return A list of storyteller roles + */ + public static List getStorytellerRoles(DiscordApi api) { + return instance.storytellerRoles.stream().map(api::getRoleById).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + } + + /** + * Checks if the user is a storyteller + * + * @param user The user to check + * @return Whether the user is the storyteller + */ + public static boolean isUserStoryteller(User user) { + return getStorytellerRoles(user.getApi()).stream().anyMatch(role -> role.getUsers().contains(user)); + } + + /** + * Checks if a message author is a storyteller + *

+ * Handy for not having to chain isPresent() and .get() + * + * @param author The author to check + * @return Whether the author is the storyteller + */ + public static boolean isMessageAuthorStoryteller(MessageAuthor author) { + return author.asUser().isPresent() && isUserStoryteller(author.asUser().get()); + } +} diff --git a/src/main/java/sheets/PlotPointHandler.java b/src/main/java/sheets/PlotPointHandler.java index 46a21a2..8b507d3 100644 --- a/src/main/java/sheets/PlotPointHandler.java +++ b/src/main/java/sheets/PlotPointHandler.java @@ -92,13 +92,13 @@ public static CompletableFuture> setPlotPointsAndLog(List> addPlotPointsToUser(int points, List> plotPointChanges, List uneditablePlayers, User user) { + public static CompletableFuture> addPlotPointsToUser(User user, int points, List> plotPointChanges, List uneditablePlayers) { final Optional oldPlotPointCount = SheetsHandler.getPlotPoints(user); if (oldPlotPointCount.isPresent()) { final int newPlotPointCount = oldPlotPointCount.get() + points; diff --git a/src/main/java/sheets/SheetsHandler.java b/src/main/java/sheets/SheetsHandler.java index 116304a..d95d28c 100644 --- a/src/main/java/sheets/SheetsHandler.java +++ b/src/main/java/sheets/SheetsHandler.java @@ -15,8 +15,8 @@ import com.google.api.services.sheets.v4.model.UpdateValuesResponse; import com.google.api.services.sheets.v4.model.ValueRange; import org.javacord.api.entity.user.User; -import players.PartyHandler; -import players.PartyMember; +import roles.Player; +import roles.PlayerHandler; import java.io.FileInputStream; import java.io.IOException; @@ -190,10 +190,9 @@ public static Optional getPlayerBleed(User user) { * @return The spreadsheet ID for the character sheet of the user, or empty if the user does not have a linked character sheet */ private static Optional getSpreadsheetForPartyMember(User user) { - return PartyHandler.getParties().stream() - .flatMap(party -> party.getMembers().stream()) - .filter(partyMember -> user.getId() == partyMember.getDiscordId()) - .map(PartyMember::getSheetId) + return PlayerHandler.getInstance().getPlayers().stream() + .filter(player -> user.getId() == player.getDiscordId()) + .map(Player::getSheetId) .findFirst(); } } diff --git a/src/main/java/statistics/ScanDice.java b/src/main/java/statistics/ScanDice.java index 1fe5dc9..7dfd64e 100644 --- a/src/main/java/statistics/ScanDice.java +++ b/src/main/java/statistics/ScanDice.java @@ -22,7 +22,7 @@ public ScanDice(Message message) { @Override public void process(StatisticsContext context) { - PoolProcessor poolOptions = new PoolProcessor(message, author); + PoolProcessor poolOptions = new PoolProcessor(author, message); if (!poolOptions.validPool()) { context.setState(new GenerateNoDiceMessage()); }