diff --git a/src/main/java/dev/pgm/community/CommunityConfig.java b/src/main/java/dev/pgm/community/CommunityConfig.java index f99981f0..f96a7c31 100644 --- a/src/main/java/dev/pgm/community/CommunityConfig.java +++ b/src/main/java/dev/pgm/community/CommunityConfig.java @@ -1,6 +1,7 @@ package dev.pgm.community; import dev.pgm.community.utils.NetworkUtils; +import dev.pgm.community.utils.ranks.RanksConfig; import org.bukkit.configuration.Configuration; import tc.oc.occ.environment.Environment; @@ -10,16 +11,20 @@ public class CommunityConfig { private String serverId; private boolean useEnvironment; private String environmentServerIdKey; + private RanksConfig ranks; + private String storeLink; public CommunityConfig(Configuration config) { reload(config); } public void reload(Configuration config) { + this.ranks = new RanksConfig(config); this.serverDisplayName = config.getString("general.server-name", ""); this.serverId = config.getString("general.server-id", ""); this.useEnvironment = config.getBoolean("general.use-environment"); this.environmentServerIdKey = config.getString("general.environment-server-id"); + this.storeLink = config.getString("general.store-link"); } public String getServerDisplayName() { @@ -39,4 +44,12 @@ public String getServerId() { public boolean isEnvironmentEnabled() { return useEnvironment && Environment.get() != null; } + + public RanksConfig getRanksConfig() { + return ranks; + } + + public String getStoreLink() { + return storeLink; + } } diff --git a/src/main/java/dev/pgm/community/CommunityPermissions.java b/src/main/java/dev/pgm/community/CommunityPermissions.java index fa97faae..228dfd5e 100644 --- a/src/main/java/dev/pgm/community/CommunityPermissions.java +++ b/src/main/java/dev/pgm/community/CommunityPermissions.java @@ -91,6 +91,10 @@ public interface CommunityPermissions { String VIEW_MAP_COOLDOWNS = ROOT + ".view-map-cooldown"; + // Super Votes + String SUPER_VOTE = REQUEST + ".super-vote"; + String SUPER_VOTE_BALANCE = SUPER_VOTE + ".balance"; + // Translations String TRANSLATE = ROOT + ".translate"; // Access to /translate diff --git a/src/main/java/dev/pgm/community/commands/graph/CommunityCommandGraph.java b/src/main/java/dev/pgm/community/commands/graph/CommunityCommandGraph.java index fe776f74..31efd31e 100644 --- a/src/main/java/dev/pgm/community/commands/graph/CommunityCommandGraph.java +++ b/src/main/java/dev/pgm/community/commands/graph/CommunityCommandGraph.java @@ -35,8 +35,10 @@ import dev.pgm.community.polls.commands.PollManagementCommands; import dev.pgm.community.polls.commands.PollVoteCommands; import dev.pgm.community.requests.commands.RequestCommands; -import dev.pgm.community.requests.commands.SponsorCommands; -import dev.pgm.community.requests.commands.TokenCommands; +import dev.pgm.community.requests.commands.sponsor.SponsorCommands; +import dev.pgm.community.requests.commands.sponsor.TokenCommands; +import dev.pgm.community.requests.commands.supervotes.SuperVoteAdminCommands; +import dev.pgm.community.requests.commands.supervotes.SuperVoteCommand; import dev.pgm.community.teleports.TeleportCommand; import dev.pgm.community.users.commands.UserInfoCommands; import dev.pgm.community.utils.CommandAudience; @@ -138,6 +140,8 @@ protected void registerCommands() { register(new RequestCommands()); register(new SponsorCommands()); register(new TokenCommands()); + register(new SuperVoteCommand()); + register(new SuperVoteAdminCommands()); // Teleport register(new TeleportCommand()); @@ -158,14 +162,11 @@ protected void registerCommands() { register(new CommunityPluginCommand()); // Help command - manager.command( - manager - .commandBuilder("community") - .literal("help") - .optional("query", StringParser.greedyStringParser()) - .handler( - context -> - minecraftHelp.queryCommands( - context.optional("query").orElse(""), context.sender()))); + manager.command(manager + .commandBuilder("community") + .literal("help") + .optional("query", StringParser.greedyStringParser()) + .handler(context -> minecraftHelp.queryCommands( + context.optional("query").orElse(""), context.sender()))); } } diff --git a/src/main/java/dev/pgm/community/requests/RequestConfig.java b/src/main/java/dev/pgm/community/requests/RequestConfig.java index e7797dc7..01a33596 100644 --- a/src/main/java/dev/pgm/community/requests/RequestConfig.java +++ b/src/main/java/dev/pgm/community/requests/RequestConfig.java @@ -25,11 +25,16 @@ public class RequestConfig extends FeatureConfigImpl { private static final String MAP_COOLDOWN_MULTIPLY = SPONSORS + ".map-cooldown"; private static final String LOWER_LIMIT_OFFSET = SPONSORS + ".lower-limit-offset"; private static final String UPPER_LIMIT_OFFSET = SPONSORS + ".upper-limit-offset"; + private static final String SUPER_VOTE = KEY + ".super-votes"; + private static final String SUPER_VOTE_ENABLED = SUPER_VOTE + ".enabled"; + private static final String SUPER_VOTE_MULTIPLIER = SUPER_VOTE + ".multiplier"; + private static final String SUPER_VOTE_BROADCAST = SUPER_VOTE + ".broadcast"; private Duration cooldown; // Cooldown for using /request private Duration sponsorCooldown; // Default cooldown for sponsor requests - private boolean sponsors; // If sponsor is enabled + private boolean sponsorEnabled; // If sponsor is enabled + private boolean superVoteEnabled; // If super votes are enabled private int dailyTokens; // Amount of tokens given on a daily basis private int weeklyTokens; // Amount of tokens given on a weekly basis @@ -45,6 +50,9 @@ public class RequestConfig extends FeatureConfigImpl { private int lowerLimitOffset; // Offset to apply on match end to lower map size bound private int upperLimitOffset; // Offset to apply on match end to upper map size bound + private int superVoteMultiplier; // The value a super vote should add + private boolean superVoteBroadcast; // If super vote activation should be broadcasted + public RequestConfig(Configuration config) { super(KEY, config); } @@ -54,7 +62,7 @@ public Duration getCooldown() { } public boolean isSponsorEnabled() { - return sponsors; + return sponsorEnabled; } public Duration getSponsorCooldown(Player player) { @@ -105,11 +113,23 @@ public int getUpperLimitOffset() { return upperLimitOffset; } + public boolean isSuperVoteEnabled() { + return superVoteEnabled; + } + + public int getSuperVoteMultiplier() { + return superVoteMultiplier; + } + + public boolean isSuperVoteBroadcast() { + return superVoteBroadcast; + } + @Override public void reload(Configuration config) { super.reload(config); this.cooldown = parseDuration(config.getString(COOLDOWN, "15s")); - this.sponsors = config.getBoolean(SPONSORS_ENABLED); + this.sponsorEnabled = config.getBoolean(SPONSORS_ENABLED); this.sponsorCooldown = parseDuration(config.getString(SPONSORS_COOLDOWN, "1h")); this.dailyTokens = config.getInt(DAILY_TOKENS); this.weeklyTokens = config.getInt(WEEKLY_TOKENS); @@ -119,5 +139,8 @@ public void reload(Configuration config) { this.mapCooldownMultiply = config.getInt(MAP_COOLDOWN_MULTIPLY); this.lowerLimitOffset = config.getInt(LOWER_LIMIT_OFFSET); this.upperLimitOffset = config.getInt(UPPER_LIMIT_OFFSET); + this.superVoteEnabled = config.getBoolean(SUPER_VOTE_ENABLED); + this.superVoteMultiplier = config.getInt(SUPER_VOTE_MULTIPLIER); + this.superVoteBroadcast = config.getBoolean(SUPER_VOTE_BROADCAST); } } diff --git a/src/main/java/dev/pgm/community/requests/RequestProfile.java b/src/main/java/dev/pgm/community/requests/RequestProfile.java index 2cc3a083..425d3355 100644 --- a/src/main/java/dev/pgm/community/requests/RequestProfile.java +++ b/src/main/java/dev/pgm/community/requests/RequestProfile.java @@ -17,8 +17,11 @@ public class RequestProfile { private int sponsorTokens; private Instant lastTokenRefreshTime; + private int superVotes; + private Instant lastSuperVote; + public RequestProfile(UUID playerId) { - this(playerId, null, null, null, null, 0, null); + this(playerId, null, null, null, null, 0, null, 0, null); } public RequestProfile( @@ -28,7 +31,9 @@ public RequestProfile( Instant lastSponsorTime, String lastSponsorMap, int sponsorTokens, - Instant lastTokenRefreshTime) { + Instant lastTokenRefreshTime, + int superVotes, + Instant lastSuperVote) { this.playerId = playerId; this.lastRequestTime = lastRequestTime; this.lastRequestMap = lastRequestMap; @@ -36,13 +41,20 @@ public RequestProfile( this.lastSponsorMap = lastSponsorMap; this.sponsorTokens = sponsorTokens; this.lastTokenRefreshTime = lastTokenRefreshTime; + this.superVotes = superVotes; + this.lastSuperVote = lastSuperVote; } - public int award(int amount) { + public int giveSponsorToken(int amount) { this.sponsorTokens = Math.max(0, sponsorTokens + amount); return sponsorTokens; } + public int giveSuperVotes(int amount) { + this.superVotes = Math.max(0, superVotes + amount); + return superVotes; + } + public void request(MapInfo map) { this.lastRequestMap = map.getId(); this.lastRequestTime = Instant.now(); @@ -51,7 +63,12 @@ public void request(MapInfo map) { public void sponsor(MapInfo map) { this.lastSponsorMap = map.getId(); this.lastSponsorTime = Instant.now(); - award(-1); + giveSponsorToken(-1); + } + + public void superVote() { + this.lastSuperVote = Instant.now(); + giveSuperVotes(-1); } public Instant getLastRequestTime() { @@ -74,12 +91,20 @@ public int getSponsorTokens() { return sponsorTokens; } + public int getSuperVotes() { + return superVotes; + } + + public Instant getLastSuperVote() { + return lastSuperVote; + } + public Instant getLastTokenRefreshTime() { return lastTokenRefreshTime; } public void refreshTokens(int amount) { - award(amount); + giveSponsorToken(amount); this.lastTokenRefreshTime = Instant.now(); } @@ -100,13 +125,15 @@ public boolean hasDayElapsed() { @Override public String toString() { return String.format( - "RequestProfile{id = %d, tokens = %d, requestMap = %s, sponsorMap = %s, lastRequest = %s, lastSponsor = %s, lastRefresh = %s}", + "RequestProfile{id = %d, tokens = %d, requestMap = %s, sponsorMap = %s, lastRequest = %s, lastSponsor = %s, lastRefresh = %s, superVotes = %s, lastSuperVote = %s}", getPlayerId().toString(), getSponsorTokens(), getLastRequestMap(), getLastSponsorMap(), getLastRequestTime().toString(), getLastSponsorTime().toString(), - getLastTokenRefreshTime().toString()); + getLastTokenRefreshTime().toString(), + getSuperVotes(), + getLastSuperVote().toString()); } } diff --git a/src/main/java/dev/pgm/community/requests/commands/SponsorCommands.java b/src/main/java/dev/pgm/community/requests/commands/sponsor/SponsorCommands.java similarity index 99% rename from src/main/java/dev/pgm/community/requests/commands/SponsorCommands.java rename to src/main/java/dev/pgm/community/requests/commands/sponsor/SponsorCommands.java index f2e1678f..0253501d 100644 --- a/src/main/java/dev/pgm/community/requests/commands/SponsorCommands.java +++ b/src/main/java/dev/pgm/community/requests/commands/sponsor/SponsorCommands.java @@ -1,4 +1,4 @@ -package dev.pgm.community.requests.commands; +package dev.pgm.community.requests.commands.sponsor; import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.text; @@ -12,7 +12,7 @@ import dev.pgm.community.requests.RequestConfig; import dev.pgm.community.requests.RequestProfile; import dev.pgm.community.requests.SponsorRequest; -import dev.pgm.community.requests.commands.TokenCommands.TokenRefreshAmount; +import dev.pgm.community.requests.commands.sponsor.TokenCommands.TokenRefreshAmount; import dev.pgm.community.requests.feature.RequestFeature; import dev.pgm.community.utils.BroadcastUtils; import dev.pgm.community.utils.CommandAudience; diff --git a/src/main/java/dev/pgm/community/requests/commands/TokenCommands.java b/src/main/java/dev/pgm/community/requests/commands/sponsor/TokenCommands.java similarity index 58% rename from src/main/java/dev/pgm/community/requests/commands/TokenCommands.java rename to src/main/java/dev/pgm/community/requests/commands/sponsor/TokenCommands.java index 6b71bca9..cf365652 100644 --- a/src/main/java/dev/pgm/community/requests/commands/TokenCommands.java +++ b/src/main/java/dev/pgm/community/requests/commands/sponsor/TokenCommands.java @@ -1,4 +1,4 @@ -package dev.pgm.community.requests.commands; +package dev.pgm.community.requests.commands.sponsor; import static net.kyori.adventure.text.Component.space; import static net.kyori.adventure.text.Component.text; @@ -42,32 +42,27 @@ public TokenCommands() { @CommandDescription("Check your token balance") public void tokens(CommandAudience audience, @Argument("target") TargetPlayer target) { if (target != null && audience.hasPermission(CommunityPermissions.TOKEN_BALANCE)) { - getTarget(target.getIdentifier(), users) - .thenAcceptAsync( - uuid -> { - if (uuid.isPresent()) { - RequestProfile profile = requests.getRequestProfile(uuid.get()).join(); - if (profile == null) { - audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); - return; - } - - Component name = users.renderUsername(uuid, NameStyle.FANCY).join(); - sendTokenBalanceMessage(audience.getAudience(), name, profile.getSponsorTokens()); - } else { - audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); - } - }); + getTarget(target.getIdentifier(), users).thenAcceptAsync(uuid -> { + if (uuid.isPresent()) { + RequestProfile profile = requests.getRequestProfile(uuid.get()).join(); + if (profile == null) { + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + return; + } + + Component name = users.renderUsername(uuid, NameStyle.FANCY).join(); + sendTokenBalanceMessage(audience.getAudience(), name, profile.getSponsorTokens()); + } else { + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + } + }); } else if (audience.isPlayer()) { Player player = audience.getPlayer(); - requests - .getRequestProfile(player.getUniqueId()) - .thenAcceptAsync( - profile -> { - int tokens = profile.getSponsorTokens(); - sendTokenBalanceMessage(audience.getAudience(), null, tokens); - sendRefreshDuration(audience.getAudience(), player, profile); - }); + requests.getRequestProfile(player.getUniqueId()).thenAcceptAsync(profile -> { + int tokens = profile.getSponsorTokens(); + sendTokenBalanceMessage(audience.getAudience(), null, tokens); + sendRefreshDuration(audience.getAudience(), player, profile); + }); } else { audience.sendWarning(text("Please provide a username to check the token balance of")); } @@ -80,43 +75,38 @@ public void give( CommandAudience audience, @Argument("target") TargetPlayer target, @Argument("amount") int amount) { - getTarget(target.getIdentifier(), users) - .thenAcceptAsync( - targetId -> { - if (targetId.isPresent()) { - RequestProfile profile = requests.getRequestProfile(targetId.get()).join(); - if (profile != null) { - int total = profile.award(amount); - requests.update(profile); - audience.sendMessage( - text() - .append(MessageUtils.TOKEN) - .append(space()) - .append( - users.renderUsername(profile.getPlayerId(), NameStyle.FANCY).join()) - .append(text(" now has ")) - .append(text(total, NamedTextColor.YELLOW, TextDecoration.BOLD)) - .append(text(" sponsor token" + (total != 1 ? "s" : ""))) - .color(NamedTextColor.GRAY) - .build()); - return; - } - } - audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); - }); + getTarget(target.getIdentifier(), users).thenAcceptAsync(targetId -> { + if (targetId.isPresent()) { + RequestProfile profile = requests.getRequestProfile(targetId.get()).join(); + if (profile != null) { + int total = profile.giveSponsorToken(amount); + requests.update(profile); + audience.sendMessage(text() + .append(MessageUtils.TOKEN) + .append(space()) + .append( + users.renderUsername(profile.getPlayerId(), NameStyle.FANCY).join()) + .append(text(" now has ")) + .append(text(total, NamedTextColor.YELLOW, TextDecoration.BOLD)) + .append(text(" sponsor token" + (total != 1 ? "s" : ""))) + .color(NamedTextColor.GRAY) + .build()); + return; + } + } + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + }); } private void sendTokenBalanceMessage(Audience viewer, Component name, int tokens) { - viewer.sendMessage( - text() - .append(MessageUtils.TOKEN) - .append(text(" ")) - .append( - name == null ? text("You have ") : name.append(text(" has ", NamedTextColor.GRAY))) - .append(text(tokens, NamedTextColor.YELLOW, TextDecoration.BOLD)) - .append(text(" sponsor token" + (tokens != 1 ? "s" : "") + ".")) - .color(NamedTextColor.GRAY) - .build()); + viewer.sendMessage(text() + .append(MessageUtils.TOKEN) + .append(text(" ")) + .append(name == null ? text("You have ") : name.append(text(" has ", NamedTextColor.GRAY))) + .append(text(tokens, NamedTextColor.YELLOW, TextDecoration.BOLD)) + .append(text(" sponsor token" + (tokens != 1 ? "s" : "") + ".")) + .color(NamedTextColor.GRAY) + .build()); } public static class TokenRefreshAmount { @@ -174,16 +164,15 @@ private void sendRefreshDuration(Audience viewer, Player player, RequestProfile return; } - viewer.sendMessage( - text() - .append(text("- ", NamedTextColor.YELLOW)) - .append(text("Next token (")) - .append(text("+", NamedTextColor.GREEN, TextDecoration.BOLD)) - .append(text(info.getAmount(), NamedTextColor.GREEN, TextDecoration.BOLD)) - .append(text(") in ")) - .append(duration(info.getDuration(), NamedTextColor.YELLOW)) - .color(NamedTextColor.GRAY) - .build()); + viewer.sendMessage(text() + .append(text("- ", NamedTextColor.YELLOW)) + .append(text("Next token (")) + .append(text("+", NamedTextColor.GREEN, TextDecoration.BOLD)) + .append(text(info.getAmount(), NamedTextColor.GREEN, TextDecoration.BOLD)) + .append(text(") in ")) + .append(duration(info.getDuration(), NamedTextColor.YELLOW)) + .color(NamedTextColor.GRAY) + .build()); } } } diff --git a/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteAdminCommands.java b/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteAdminCommands.java new file mode 100644 index 00000000..9df46c30 --- /dev/null +++ b/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteAdminCommands.java @@ -0,0 +1,96 @@ +package dev.pgm.community.requests.commands.supervotes; + +import static net.kyori.adventure.text.Component.space; +import static net.kyori.adventure.text.Component.text; + +import dev.pgm.community.Community; +import dev.pgm.community.CommunityCommand; +import dev.pgm.community.CommunityPermissions; +import dev.pgm.community.commands.player.TargetPlayer; +import dev.pgm.community.requests.RequestProfile; +import dev.pgm.community.requests.feature.RequestFeature; +import dev.pgm.community.requests.supervotes.SuperVoteComponents; +import dev.pgm.community.users.feature.UsersFeature; +import dev.pgm.community.utils.CommandAudience; +import dev.pgm.community.utils.MessageUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.entity.Player; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.Argument; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.Command; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.CommandDescription; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.Permission; +import tc.oc.pgm.util.named.NameStyle; + +@Command("supervotes") +public class SuperVoteAdminCommands extends CommunityCommand { + + private final UsersFeature users; + private final RequestFeature requests; + + public SuperVoteAdminCommands() { + this.users = Community.get().getFeatures().getUsers(); + this.requests = Community.get().getFeatures().getRequests(); + } + + @Command("[target]") + @CommandDescription("Check your super vote balance") + public void balance(CommandAudience audience, @Argument(value = "target") TargetPlayer target) { + if (target != null && audience.hasPermission(CommunityPermissions.SUPER_VOTE_BALANCE)) { + getTarget(target.getIdentifier(), users).thenAcceptAsync(uuid -> { + if (uuid.isPresent()) { + RequestProfile profile = requests.getRequestProfile(uuid.get()).join(); + if (profile == null) { + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + return; + } + + Component name = users.renderUsername(uuid, NameStyle.FANCY).join(); + audience.sendMessage( + SuperVoteComponents.getSuperVoteBalance(name, profile.getSuperVotes())); + } else { + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + } + }); + } else if (audience.isPlayer()) { + Player player = audience.getPlayer(); + requests.getRequestProfile(player.getUniqueId()).thenAcceptAsync(profile -> { + int superVotes = profile.getSuperVotes(); + audience.sendMessage(SuperVoteComponents.getSuperVoteBalance(superVotes)); + }); + } else { + audience.sendWarning(text("Please provide a username to check the super vote balance of.")); + } + } + + @Command("give ") + @CommandDescription("Give the target player super votes") + @Permission(CommunityPermissions.ADMIN) + public void give( + CommandAudience audience, + @Argument("target") TargetPlayer target, + @Argument("amount") int amount) { + getTarget(target.getIdentifier(), users).thenAcceptAsync(targetId -> { + if (targetId.isPresent()) { + RequestProfile profile = requests.getRequestProfile(targetId.get()).join(); + if (profile != null) { + int total = profile.giveSuperVotes(amount); + requests.update(profile); + audience.sendMessage(text() + .append(MessageUtils.VOTE) + .append(space()) + .append( + users.renderUsername(profile.getPlayerId(), NameStyle.FANCY).join()) + .append(text(" now has ")) + .append(text(total, NamedTextColor.DARK_PURPLE, TextDecoration.BOLD)) + .append(text(" super vote" + (total != 1 ? "s" : ""))) + .color(NamedTextColor.GRAY) + .build()); + return; + } + } + audience.sendWarning(formatNotFoundComponent(target.getIdentifier())); + }); + } +} diff --git a/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteCommand.java b/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteCommand.java new file mode 100644 index 00000000..84b4289c --- /dev/null +++ b/src/main/java/dev/pgm/community/requests/commands/supervotes/SuperVoteCommand.java @@ -0,0 +1,24 @@ +package dev.pgm.community.requests.commands.supervotes; + +import dev.pgm.community.Community; +import dev.pgm.community.CommunityCommand; +import dev.pgm.community.requests.feature.RequestFeature; +import dev.pgm.community.utils.CommandAudience; +import org.bukkit.entity.Player; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.Command; +import tc.oc.pgm.lib.org.incendo.cloud.annotations.CommandDescription; + +public class SuperVoteCommand extends CommunityCommand { + + private final RequestFeature requests; + + public SuperVoteCommand() { + this.requests = Community.get().getFeatures().getRequests(); + } + + @Command("supervote") + @CommandDescription("Activate a supervote for the active map vote") + public void superVote(CommandAudience audience, Player sender) { + requests.superVote(sender); + } +} diff --git a/src/main/java/dev/pgm/community/requests/feature/RequestFeature.java b/src/main/java/dev/pgm/community/requests/feature/RequestFeature.java index 89ad7692..755a5630 100644 --- a/src/main/java/dev/pgm/community/requests/feature/RequestFeature.java +++ b/src/main/java/dev/pgm/community/requests/feature/RequestFeature.java @@ -44,6 +44,13 @@ public interface RequestFeature extends Feature { */ void sponsor(Player player, MapInfo map); // For premium sponsor auto-add + /** + * Activate a super vote for the current map vote + * + * @param player the supervoter + */ + void superVote(Player player); + /** * Get whether the provided playerId can submit a request * @@ -60,6 +67,22 @@ public interface RequestFeature extends Feature { */ boolean canSponsor(Player player); // If player can sponsor + /** + * Get whether the provided player can activate a super vote + * + * @param player + * @return true if the player can activate a super vote, false if not + */ + boolean canSuperVote(Player player); + + /** + * Get whether the provided player has activated a super vote + * + * @param player + * @return true if the player has activated a super vote, false if not + */ + boolean isSuperVoteActive(Player player); + /** * Submit a database update to the provided {@link RequestProfile} * @@ -152,4 +175,8 @@ public interface RequestFeature extends Feature { List getAvailableSponsorMaps(); MapSizeBounds getCurrentMapSizeBounds(); + + int getStandardExtraVoteLevel(Player player); + + int getMultipliedExtraVoteLevel(Player player); } diff --git a/src/main/java/dev/pgm/community/requests/feature/RequestFeatureBase.java b/src/main/java/dev/pgm/community/requests/feature/RequestFeatureBase.java index f4736947..7e2d021d 100644 --- a/src/main/java/dev/pgm/community/requests/feature/RequestFeatureBase.java +++ b/src/main/java/dev/pgm/community/requests/feature/RequestFeatureBase.java @@ -24,8 +24,10 @@ import dev.pgm.community.requests.RequestProfile; import dev.pgm.community.requests.SponsorRequest; import dev.pgm.community.requests.menu.SponsorMenu; +import dev.pgm.community.requests.supervotes.SuperVoteManager; import dev.pgm.community.users.feature.UsersFeature; import dev.pgm.community.utils.BroadcastUtils; +import dev.pgm.community.utils.MessageUtils; import dev.pgm.community.utils.PGMUtils; import dev.pgm.community.utils.PGMUtils.MapSizeBounds; import dev.pgm.community.utils.Sounds; @@ -85,6 +87,8 @@ public abstract class RequestFeatureBase extends FeatureBase implements RequestF private SponsorRequest currentSponsor; private SponsorVotingBookCreator bookCreator; + private SuperVoteManager superVotes; + private boolean accepting; public RequestFeatureBase( @@ -99,6 +103,7 @@ public RequestFeatureBase( this.sponsors = Lists.newLinkedList(); this.currentSponsor = null; this.bookCreator = new SponsorVotingBookCreator(this); + this.superVotes = new SuperVoteManager(config, logger); if (getConfig().isEnabled() && PGMUtils.isPGMEnabled()) { enable(); @@ -147,20 +152,24 @@ public Queue getSponsorQueue() { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { + final Player player = event.getPlayer(); + + superVotes.onRelogin(player); + onLogin(event).thenAcceptAsync(profile -> { - if (event.getPlayer().hasPermission(CommunityPermissions.REQUEST_SPONSOR)) { + if (player.hasPermission(CommunityPermissions.REQUEST_SPONSOR)) { int refresh = 0; boolean daily = false; // Check permission to determine what amount to refresh // Always prefer the daily compared to the weekly (e.g sponsor inherits donor perms, // only give daily not weekly) - if (event.getPlayer().hasPermission(CommunityPermissions.TOKEN_DAILY)) { + if (player.hasPermission(CommunityPermissions.TOKEN_DAILY)) { if (profile.hasDayElapsed()) { refresh = getRequestConfig().getDailyTokenAmount(); daily = true; } - } else if (event.getPlayer().hasPermission(CommunityPermissions.TOKEN_WEEKLY)) { + } else if (player.hasPermission(CommunityPermissions.TOKEN_WEEKLY)) { if (profile.hasWeekElapsed()) { refresh = getRequestConfig().getWeeklyTokenAmount(); } @@ -170,8 +179,7 @@ public void onPlayerJoin(PlayerJoinEvent event) { if (refresh > 0 && profile.getSponsorTokens() < getRequestConfig().getMaxTokens()) { profile.refreshTokens(refresh); // Set new token amount update(profile); // Save new token balance to database - sendDelayedTokenRefreshMessage( - event.getPlayer(), refresh, daily, profile.getSponsorTokens()); + sendDelayedTokenRefreshMessage(player, refresh, daily, profile.getSponsorTokens()); } } }); @@ -201,6 +209,8 @@ private SponsorRequest getNextSponsor() { @EventHandler public void onMatchEnd(MatchFinishEvent event) { + superVotes.onVoteStart(); + if (currentSponsor != null) { // Reset current sponsor after match ends currentSponsor = null; } @@ -252,13 +262,15 @@ public void onMatchEnd(MatchFinishEvent event) { @EventHandler public void onVoteEnd(MatchVoteFinishEvent event) { + superVotes.onVoteEnd(); + if (currentSponsor != null) { Player player = Bukkit.getPlayer(currentSponsor.getPlayerId()); // Same map = winner, refund the token even if offline if (currentSponsor.getMap().equals(event.getPickedMap()) && currentSponsor.canRefund()) { getRequestProfile(currentSponsor.getPlayerId()).thenAcceptAsync(profile -> { - profile.award(1); + profile.giveSponsorToken(1); update(profile); if (player != null) { @@ -486,6 +498,46 @@ public void sponsor(Player player, MapInfo map) { }); } + @Override + public void superVote(Player player) { + Audience viewer = Audience.get(player); + + if (!getRequestConfig().isSuperVoteEnabled()) { + viewer.sendWarning(text("Super votes are not enabled!")); + return; + } + + if (!canSuperVote(player)) { + viewer.sendWarning(text("You have already activated a super vote for this vote!")); + return; + } + + if (!superVotes.isVotingActive()) { + viewer.sendWarning(text("You can only activate a super vote after the match ends!")); + return; + } + + getRequestProfile(player.getUniqueId()).thenAcceptAsync(profile -> { + if (profile.getSuperVotes() < 1) { + viewer.sendWarning(text("You don't have enough super votes!")); + return; + } + + // Save profile + profile.superVote(); + update(profile); + + // Activate permissions + superVotes.onActivate(player); + + // Broadcast or send message + sendSuperVoterActivationFeedback(player); + }); + + // Re-Open the vote book + Bukkit.dispatchCommand(player, "votebook"); + } + @Override public boolean canRequest(UUID playerId) { return cooldown.getIfPresent(playerId) == null; @@ -510,6 +562,16 @@ public Set getRequesters(MapInfo map) { .collect(Collectors.toSet()); } + @Override + public boolean canSuperVote(Player player) { + return !isSuperVoteActive(player); + } + + @Override + public boolean isSuperVoteActive(Player player) { + return superVotes.isActive(player); + } + @Override public int clearRequests(MapInfo map) { Set requesters = getRequesters(map); @@ -611,6 +673,16 @@ public Map getMapCooldowns() { return mapCooldown; } + @Override + public int getStandardExtraVoteLevel(Player player) { + return superVotes.getVoteLevel(player); + } + + @Override + public int getMultipliedExtraVoteLevel(Player player) { + return superVotes.getMultipliedVoteLevel(player); + } + private Component getCooldownMessage(Instant lastRequest, Duration cooldownTime) { Duration timeLeft = cooldownTime.minus(Duration.between(lastRequest, Instant.now())); @@ -798,4 +870,24 @@ private Component getMapSizeBoundsComponent() { .append(text(")", NamedTextColor.GRAY)) .build(); } + + private void sendSuperVoterActivationFeedback(Player player) { + Audience viewer = Audience.get(player); + if (getRequestConfig().isSuperVoteBroadcast()) { + Component alert = text() + .append(MessageUtils.VOTE) + .append(player(player, NameStyle.FANCY)) + .append(text(" has activated a ", NamedTextColor.YELLOW)) + .append(text("super vote", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD)) + .build(); + + BroadcastUtils.sendGlobalMessage(alert); + } else { + viewer.sendMessage(text() + .append(MessageUtils.VOTE) + .append(text("You activated a ", NamedTextColor.YELLOW)) + .append(text("super vote", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD)) + .append(text("!", NamedTextColor.YELLOW))); + } + } } diff --git a/src/main/java/dev/pgm/community/requests/feature/SponsorVotingBookCreator.java b/src/main/java/dev/pgm/community/requests/feature/SponsorVotingBookCreator.java index 6680910d..35377774 100644 --- a/src/main/java/dev/pgm/community/requests/feature/SponsorVotingBookCreator.java +++ b/src/main/java/dev/pgm/community/requests/feature/SponsorVotingBookCreator.java @@ -4,8 +4,17 @@ import static net.kyori.adventure.text.Component.text; import static tc.oc.pgm.util.player.PlayerComponent.player; +import dev.pgm.community.Community; +import dev.pgm.community.requests.RequestProfile; import dev.pgm.community.requests.SponsorRequest; +import dev.pgm.community.requests.supervotes.SuperVoteComponents; +import dev.pgm.community.utils.MessageUtils; +import dev.pgm.community.utils.ranks.RanksConfig.Rank; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import tc.oc.pgm.api.map.MapInfo; @@ -39,4 +48,130 @@ public ComponentLike getHover(MatchPlayer viewer, MapInfo map, boolean voted) { return originalHover; } + + @Override + public Component getMapBookFooter(MatchPlayer viewer) { + boolean active = manager.isSuperVoteActive(viewer.getBukkit()); + int standard = manager.getStandardExtraVoteLevel(viewer.getBukkit()); + int superLevel = manager.getMultipliedExtraVoteLevel(viewer.getBukkit()); + + // a 0 should display as 1x + if (standard < 1) standard = 1; + + RequestProfile profile = manager.getCached(viewer.getId()); + int remaining = profile != null ? profile.getSuperVotes() : 0; + boolean canUse = manager.canSuperVote(viewer.getBukkit()) && remaining > 0; + + Component votePrefix = text() + .append(MessageUtils.VOTE) + .hoverEvent(HoverEvent.showText(SuperVoteComponents.getSuperVoteBalance(remaining))) + .build(); + + TextComponent.Builder builder = text() + .appendNewline() + .appendNewline() + .append(votePrefix) + .appendSpace() + .append( + active + ? getSuperVoteActiveComponent() + : getSuperVoteButtonComponent(canUse, remaining, superLevel)) + .appendNewline() + .appendNewline() + .append(getVoteMultiplierComponent(active ? superLevel : standard)); + + return builder.build(); + } + + private Component getSuperVoteActiveComponent() { + return text() + .append(text("Super Vote", NamedTextColor.LIGHT_PURPLE)) + .appendSpace() + .append(text("active!", NamedTextColor.GREEN)) + .build(); + } + + private Component getSuperVoteButtonComponent(boolean canUse, int balance, int multiplier) { + + Component activateHover = text() + .append(text("Click to activate a", NamedTextColor.GRAY)) + .appendSpace() + .append(text(multiplier + "x", NamedTextColor.LIGHT_PURPLE)) + .appendSpace() + .append(text("super vote!", NamedTextColor.GRAY)) + .build(); + + if (!canUse) { + activateHover = text() + .append(text("You don't have any super votes available!", NamedTextColor.GRAY)) + .appendSpace() + .append(text("Visit ", NamedTextColor.GRAY)) + .append(MessageUtils.getStoreLink()) + .append(text(" to purchase more.", NamedTextColor.GRAY)) + .build(); + } + + TextComponent.Builder builder = text() + .append(text("[", NamedTextColor.GRAY)) + .append(text( + "Super Vote", + canUse ? NamedTextColor.LIGHT_PURPLE : NamedTextColor.DARK_GRAY, + TextDecoration.BOLD)) + .append(text("]", NamedTextColor.GRAY)) + .hoverEvent(HoverEvent.showText(activateHover)); + + if (canUse) { + builder.clickEvent(ClickEvent.runCommand("/supervote")); + } else { + builder.clickEvent(ClickEvent.openUrl(Community.get().getServerConfig().getStoreLink())); + } + + return builder.build(); + } + + private Component getVoteMultiplierComponent(int multiplier) { + + TextComponent.Builder stdHoverBuilder = text(); + + for (Rank rank : Community.get().getServerConfig().getRanksConfig().getRanks()) { + Component rankComponent = text() + .append(text(rank.getPrefix() + " " + rank.getName(), rank.getTextColor())) + .append(text(" has a ", NamedTextColor.GRAY)) + .append(text(rank.getVoteMultiplier() + "x", rank.getTextColor(), TextDecoration.BOLD)) + .append(text(" multiplier", NamedTextColor.GRAY)) + .build(); + + stdHoverBuilder.append(rankComponent).appendNewline(); + } + + Component supervote = text() + .append(MessageUtils.VOTE) + .append(text(" Super Vote", NamedTextColor.LIGHT_PURPLE)) + .append(text(" adds ", NamedTextColor.GRAY)) + .append(text("3x", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD)) + .append(text(" to your multiplier", NamedTextColor.GRAY)) + .build(); + + Component shop = text() + .append(text("Visit ", NamedTextColor.GRAY)) + .append(MessageUtils.getStoreLink()) + .append(text(" to learn more", NamedTextColor.GRAY)) + .build(); + + Component stdHover = stdHoverBuilder + .append(supervote) + .appendNewline() + .appendNewline() + .append(shop) + .build(); + + return text() + .append(text("Vote Multiplier", NamedTextColor.DARK_AQUA)) + .append(text(":", NamedTextColor.GRAY)) + .appendSpace() + .append(text(multiplier + "x", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD)) + .hoverEvent(HoverEvent.showText(stdHover)) + .clickEvent(ClickEvent.openUrl(Community.get().getServerConfig().getStoreLink())) + .build(); + } } diff --git a/src/main/java/dev/pgm/community/requests/services/RequestQuery.java b/src/main/java/dev/pgm/community/requests/services/RequestQuery.java index 0b0f9488..a0579dcf 100644 --- a/src/main/java/dev/pgm/community/requests/services/RequestQuery.java +++ b/src/main/java/dev/pgm/community/requests/services/RequestQuery.java @@ -3,16 +3,15 @@ public interface RequestQuery { static final String TABLE_FIELDS = - "(id VARCHAR(36) PRIMARY KEY, last_request_time LONG, last_request_map VARCHAR(255), last_sponsor_time LONG, last_sponsor_map VARCHAR(255), tokens INT, last_token_refresh LONG)"; + "(id VARCHAR(36) PRIMARY KEY, last_request_time LONG, last_request_map VARCHAR(255), last_sponsor_time LONG, last_sponsor_map VARCHAR(255), tokens INT, last_token_refresh LONG, super_votes INT, last_super_vote LONG)"; static final String TABLE_NAME = "requests"; static final String INSERT_REQUESTS_QUERY = - "INSERT INTO " + TABLE_NAME + " VALUES (?,?,?,?,?,?,?)"; + "INSERT INTO " + TABLE_NAME + " VALUES (?,?,?,?,?,?,?,?,?)"; - static final String UPDATE_REQUEST_QUERY = - "UPDATE " - + TABLE_NAME - + " SET last_request_time = ?, last_request_map = ?, last_sponsor_time = ?, last_sponsor_map = ?, tokens = ?, last_token_refresh = ? WHERE id = ? "; + static final String UPDATE_REQUEST_QUERY = "UPDATE " + + TABLE_NAME + + " SET last_request_time = ?, last_request_map = ?, last_sponsor_time = ?, last_sponsor_map = ?, tokens = ?, last_token_refresh = ?, super_votes = ?, last_super_vote = ? WHERE id = ? "; static final String SELECT_REQUEST_QUERY = "SELECT * from " + TABLE_NAME + " WHERE id = ? LIMIT 1"; diff --git a/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java b/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java index 935a41ad..853b7556 100644 --- a/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java +++ b/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java @@ -21,28 +21,23 @@ public class SQLRequestService extends SQLFeatureBase public SQLRequestService() { super(TABLE_NAME, TABLE_FIELDS); - this.profileCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public UserRequestData load(UUID key) throws Exception { - return new UserRequestData(key); - } - }); + this.profileCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public UserRequestData load(UUID key) throws Exception { + return new UserRequestData(key); + } + }); } public CompletableFuture login(UUID playerId) { - return query(playerId.toString()) - .thenApplyAsync( - profile -> { - RequestProfile reqProfile = new RequestProfile(playerId); - if (profile == null) { - save(reqProfile); - return reqProfile; - } - return profile; - }); + return query(playerId.toString()).thenApplyAsync(profile -> { + RequestProfile reqProfile = new RequestProfile(playerId); + if (profile == null) { + save(reqProfile); + return reqProfile; + } + return profile; + }); } @Nullable @@ -64,7 +59,9 @@ public void save(RequestProfile profile) { convertTime(profile.getLastSponsorTime()), profile.getLastSponsorMap(), profile.getSponsorTokens(), - convertTime(profile.getLastTokenRefreshTime())); + convertTime(profile.getLastTokenRefreshTime()), + profile.getSuperVotes(), + convertTime(profile.getLastSuperVote())); profileCache.invalidate(profile.getPlayerId()); } @@ -77,6 +74,8 @@ public void update(RequestProfile profile) { profile.getLastSponsorMap(), profile.getSponsorTokens(), convertTime(profile.getLastTokenRefreshTime()), + profile.getSuperVotes(), + convertTime(profile.getLastSuperVote()), profile.getPlayerId().toString()); } @@ -94,37 +93,41 @@ public CompletableFuture query(String target) { return CompletableFuture.completedFuture(profile.getProfile()); } else { return DB.getFirstRowAsync(SELECT_REQUEST_QUERY, playerId.toString()) - .thenApplyAsync( - result -> { - if (result != null) { - final UUID id = UUID.fromString(result.getString("id")); - final long lastRequest = Long.parseLong(result.getString("last_request_time")); - final String lastRequestMap = result.getString("last_request_map"); - final long lastSponsor = Long.parseLong(result.getString("last_sponsor_time")); - final String lastSponsorMap = result.getString("last_sponsor_map"); - final int tokens = result.getInt("tokens"); - final long lastToken = Long.parseLong(result.getString("last_token_refresh")); - - final Instant lastRequestTime = - lastRequest == -1 ? null : Instant.ofEpochMilli(lastRequest); - final Instant lastSponsorTime = - lastSponsor == -1 ? null : Instant.ofEpochMilli(lastSponsor); - final Instant lastTokenRefreshTime = - lastToken == -1 ? null : Instant.ofEpochMilli(lastToken); - - profile.setProfile( - new RequestProfile( - id, - lastRequestTime, - lastRequestMap, - lastSponsorTime, - lastSponsorMap, - tokens, - lastTokenRefreshTime)); - } - profile.setLoaded(true); - return profile.getProfile(); - }); + .thenApplyAsync(result -> { + if (result != null) { + final UUID id = UUID.fromString(result.getString("id")); + final long lastRequest = Long.parseLong(result.getString("last_request_time")); + final String lastRequestMap = result.getString("last_request_map"); + final long lastSponsor = Long.parseLong(result.getString("last_sponsor_time")); + final String lastSponsorMap = result.getString("last_sponsor_map"); + final int tokens = result.getInt("tokens"); + final long lastToken = Long.parseLong(result.getString("last_token_refresh")); + final int superVotes = result.getInt("super_votes"); + final long lastSuperVote = Long.parseLong(result.getString("last_super_vote")); + + final Instant lastRequestTime = + lastRequest == -1 ? null : Instant.ofEpochMilli(lastRequest); + final Instant lastSponsorTime = + lastSponsor == -1 ? null : Instant.ofEpochMilli(lastSponsor); + final Instant lastTokenRefreshTime = + lastToken == -1 ? null : Instant.ofEpochMilli(lastToken); + final Instant lastSuperVoteTime = + lastSuperVote == -1 ? null : Instant.ofEpochMilli(lastSuperVote); + + profile.setProfile(new RequestProfile( + id, + lastRequestTime, + lastRequestMap, + lastSponsorTime, + lastSponsorMap, + tokens, + lastTokenRefreshTime, + superVotes, + lastSuperVoteTime)); + } + profile.setLoaded(true); + return profile.getProfile(); + }); } } diff --git a/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteComponents.java b/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteComponents.java new file mode 100644 index 00000000..dbf6541a --- /dev/null +++ b/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteComponents.java @@ -0,0 +1,38 @@ +package dev.pgm.community.requests.supervotes; + +import static net.kyori.adventure.text.Component.text; + +import dev.pgm.community.utils.MessageUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; + +public class SuperVoteComponents { + + public static Component getSuperVoteBalance(int superVotes) { + return getSuperVoteBalance(null, superVotes); + } + + public static Component getSuperVoteBalance(Component name, int superVotes) { + Component prefix = MessageUtils.VOTE; + + Component player = name == null + ? text("You have ", NamedTextColor.GRAY) + : text().append(name).append(text(" has ", NamedTextColor.GRAY)).build(); + + Component amount = text(superVotes, NamedTextColor.DARK_PURPLE, TextDecoration.BOLD); + + Component votes = text(" super vote" + (superVotes != 1 ? "s" : ""), NamedTextColor.GRAY); + + Component remain = text(" remaining", NamedTextColor.GRAY); + + return text() + .append(prefix) + .appendSpace() + .append(player) + .append(amount) + .append(votes) + .append(remain) + .build(); + } +} diff --git a/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteManager.java b/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteManager.java new file mode 100644 index 00000000..59a968f7 --- /dev/null +++ b/src/main/java/dev/pgm/community/requests/supervotes/SuperVoteManager.java @@ -0,0 +1,146 @@ +package dev.pgm.community.requests.supervotes; + +import com.google.common.collect.Sets; +import dev.pgm.community.Community; +import dev.pgm.community.requests.RequestConfig; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachment; +import tc.oc.pgm.api.PGM; + +public class SuperVoteManager { + + private final RequestConfig config; + private final Set activeSuperVoters; + private boolean isVoteActive; + private final Logger logger; + private final Map playerPermissions; + + public SuperVoteManager(RequestConfig config, Logger logger) { + this.config = config; + this.logger = logger; + this.activeSuperVoters = Sets.newHashSet(); + this.playerPermissions = new HashMap<>(); + } + + public boolean isVotingActive() { + return isVoteActive; + } + + public void onVoteStart() { + isVoteActive = true; + activeSuperVoters.clear(); + } + + public void onVoteEnd() { + isVoteActive = false; + removeAllSuperVotePermissions(); + } + + public void onActivate(Player player) { + activeSuperVoters.add(player.getUniqueId()); + applySuperVotePermissions(player); + } + + public void onRelogin(Player player) { + if (isActive(player)) { + applySuperVotePermissions(player); + } + } + + public void onLeave(Player player) { + if (playerPermissions.containsKey(player.getUniqueId())) { + removeSuperVotePermissions(player); + } + } + + private String getExtraVotePermission(int level) { + return "pgm.vote.extra." + level; + } + + private void applySuperVotePermissions(Player player) { + int multipliedVoteLevel = getMultipliedVoteLevel(player); + StoredPermission permission = + new StoredPermission(player, getExtraVotePermission(multipliedVoteLevel)); + permission.enable(); + playerPermissions.put(player.getUniqueId(), permission); + + // DEBUG: + player.sendMessage(ChatColor.GREEN + + "You now have " + + ChatColor.AQUA + + " pgm.vote.extra." + + multipliedVoteLevel); + } + + private void removeSuperVotePermissions(Player player) { + StoredPermission permission = playerPermissions.remove(player.getUniqueId()); + if (permission != null) { + permission.disable(player); + + // DEBUG: + player.sendMessage( + ChatColor.RED + "You no longer have " + ChatColor.AQUA + permission.getPermission()); + } + } + + public void removeAllSuperVotePermissions() { + logger.info("Total active super voters = " + activeSuperVoters.size()); + + for (UUID playerId : activeSuperVoters) { + Player player = Bukkit.getPlayer(playerId); + if (player != null) { + removeSuperVotePermissions(player); + + logger.info("Removing super vote permissions from " + playerId); + } + } + this.activeSuperVoters.clear(); + } + + public int getMultipliedVoteLevel(Player player) { + return getVoteLevel(player) + config.getSuperVoteMultiplier(); + } + + public int getVoteLevel(Player player) { + for (int level = PGM.get().getConfiguration().getMaxExtraVotes(); level > 1; level--) { + if (player.hasPermission("pgm.vote.extra." + level)) { + return level; + } + } + return 0; + } + + public boolean isActive(Player player) { + return this.activeSuperVoters.contains(player.getUniqueId()); + } + + private class StoredPermission { + private PermissionAttachment attachment; + private String permission; + + public StoredPermission(Player player, String permission) { + this.attachment = player.addAttachment(Community.get()); + this.permission = permission; + } + + public void enable() { + attachment.setPermission(permission, true); + } + + public void disable(Player player) { + attachment.setPermission(permission, false); + player.removeAttachment(attachment); + } + + public String getPermission() { + return permission; + } + } +} diff --git a/src/main/java/dev/pgm/community/utils/MessageUtils.java b/src/main/java/dev/pgm/community/utils/MessageUtils.java index 1a85f437..5606e9f7 100644 --- a/src/main/java/dev/pgm/community/utils/MessageUtils.java +++ b/src/main/java/dev/pgm/community/utils/MessageUtils.java @@ -5,6 +5,7 @@ import static tc.oc.pgm.util.text.TemporalComponent.duration; import com.google.common.collect.Lists; +import dev.pgm.community.Community; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -22,11 +23,22 @@ public class MessageUtils { + public static final String VOTE_SYMBOL = "Ⓢ"; public static final String TOKEN_SYMBOL = "✪"; public static final Component DENY = text("\u2715", NamedTextColor.DARK_RED); public static final Component ACCEPT = text("\u2714", NamedTextColor.GREEN); public static final Component WARNING = text("\u26a0", NamedTextColor.YELLOW); public static final Component TOKEN = text(TOKEN_SYMBOL, NamedTextColor.GOLD); + public static final Component VOTE = text(VOTE_SYMBOL, NamedTextColor.LIGHT_PURPLE); + + public static final Component getStoreLink() { + return text() + .append(text( + Community.get().getServerConfig().getStoreLink(), + NamedTextColor.AQUA, + TextDecoration.UNDERLINED)) + .build(); + } public static String formatKickScreenMessage(String headerTitle, List lines) { List message = Lists.newArrayList(); @@ -34,10 +46,8 @@ public static String formatKickScreenMessage(String headerTitle, List Component header = text(LegacyFormatUtils.horizontalLineHeading(headerTitle, ChatColor.DARK_GRAY)); - Component footer = - text( - LegacyFormatUtils.horizontalLine( - ChatColor.DARK_GRAY, LegacyFormatUtils.MAX_CHAT_WIDTH)); + Component footer = text( + LegacyFormatUtils.horizontalLine(ChatColor.DARK_GRAY, LegacyFormatUtils.MAX_CHAT_WIDTH)); message.add(header); // Header Line - FIRST lines.forEach(message::add); // Add messages @@ -67,17 +77,13 @@ public static Component formatTokenTransaction(int amount, Component message) { public static Component formatTokenTransaction(int amount, Component message, Component hover) { boolean add = amount > 0; - TextComponent.Builder builder = - text() - .append( - text( - add ? "+" : "-", - add ? NamedTextColor.GREEN : NamedTextColor.RED, - TextDecoration.BOLD)) - .append(text(Math.abs(amount) + " ", NamedTextColor.YELLOW, TextDecoration.BOLD)) - .append(TOKEN) - .append(space()) - .append(message); + TextComponent.Builder builder = text() + .append(text( + add ? "+" : "-", add ? NamedTextColor.GREEN : NamedTextColor.RED, TextDecoration.BOLD)) + .append(text(Math.abs(amount) + " ", NamedTextColor.YELLOW, TextDecoration.BOLD)) + .append(TOKEN) + .append(space()) + .append(message); if (hover != null) { builder.hoverEvent(HoverEvent.showText(hover)); diff --git a/src/main/java/dev/pgm/community/utils/ranks/RankUtils.java b/src/main/java/dev/pgm/community/utils/ranks/RankUtils.java new file mode 100644 index 00000000..710c52f3 --- /dev/null +++ b/src/main/java/dev/pgm/community/utils/ranks/RankUtils.java @@ -0,0 +1,26 @@ +package dev.pgm.community.utils.ranks; + +import dev.pgm.community.Community; +import java.util.List; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class RankUtils { + + @Nullable + public static RanksConfig.Rank getHighestLevelRank(Player player) { + List allRanks = + Community.get().getServerConfig().getRanksConfig().getRanks(); + RanksConfig.Rank highestRank = null; + + for (RanksConfig.Rank rank : allRanks) { + if (player.hasPermission(rank.getPermission())) { + if (highestRank == null || rank.getWeight() > highestRank.getWeight()) { + highestRank = rank; + } + } + } + + return highestRank; + } +} diff --git a/src/main/java/dev/pgm/community/utils/ranks/RanksConfig.java b/src/main/java/dev/pgm/community/utils/ranks/RanksConfig.java new file mode 100644 index 00000000..a1b1fe38 --- /dev/null +++ b/src/main/java/dev/pgm/community/utils/ranks/RanksConfig.java @@ -0,0 +1,97 @@ +package dev.pgm.community.utils.ranks; + +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.ChatColor; +import org.bukkit.configuration.Configuration; +import tc.oc.pgm.util.text.TextFormatter; + +public class RanksConfig { + + private static final String RANKS_KEY = "ranks"; + + private List ranks; + + public RanksConfig(Configuration config) { + reload(config); + } + + public List getRanks() { + return ranks; + } + + public void reload(Configuration config) { + ranks = Lists.newArrayList(); + + Set rankNames = config.getConfigurationSection(RANKS_KEY).getKeys(false); + for (String rankName : rankNames) { + String name = config.getString(RANKS_KEY + "." + rankName + ".name"); + String prefix = config.getString(RANKS_KEY + "." + rankName + ".prefix"); + String permission = config.getString(RANKS_KEY + "." + rankName + ".permission"); + String colorString = config.getString(RANKS_KEY + "." + rankName + ".color"); + ChatColor color = ChatColor.valueOf(colorString.toUpperCase()); + int multiplier = config.getInt(RANKS_KEY + "." + rankName + ".multiplier"); + int weight = config.getInt(RANKS_KEY + "." + rankName + ".weight"); + + Rank rank = new Rank(name, prefix, permission, color, multiplier, weight); + ranks.add(rank); + } + + Collections.sort(ranks, Comparator.comparingInt(Rank::getWeight)); + } + + public static class Rank { + private String name; + private String prefix; + private String permission; + private ChatColor color; + private int multiplier; + private int weight; + + public Rank( + String name, + String prefix, + String permission, + ChatColor color, + int multiplier, + int weight) { + this.name = name; + this.prefix = prefix; + this.color = color; + this.multiplier = multiplier; + this.weight = weight; + } + + public String getName() { + return name; + } + + public String getPrefix() { + return prefix; + } + + public String getPermission() { + return permission; + } + + public ChatColor getColor() { + return color; + } + + public NamedTextColor getTextColor() { + return TextFormatter.convert(getColor()); + } + + public int getVoteMultiplier() { + return multiplier; + } + + public int getWeight() { + return weight; + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e2f7eb02..3fd1de45 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -14,7 +14,27 @@ general: # The specific key ID used for fetching the server ID from the Environment API. environment-server-id: "server-id" + + # Store link used to advertise in specfic messages + store-link: "https://oc.tc/store" +# Ranks +ranks: + supporter: + name: "Supporter" + permission: "community.rank.supporter" + prefix: "*" + color: "GREEN" + multiplier: 2 + weight: 0 + sponsor: + name: "Sponsor" + permission: "community.rank.sponsor" + prefix: "+" + color: "YELLOW" + multiplier: 3 + weight: 1 + # Moderation - Settings related to punishments (/warn, /kick, /ban, /mute) moderation: enabled: true # Whether punishments are enabled @@ -213,6 +233,11 @@ requests: lower-limit-offset: 4 # Subtracted from the current minimum upper-limit-offset: 8 # Added to the current maximum + super-votes: + enabled: true # Whether /supervote is enabled + multiplier: 3 # The value of a super vote (how much to boost existing multiplier) + broadcast: true # If super vote activation should be broadcast globally + # Mobs - Spawn creatures that will attack players ;) mobs: enabled: true