From 7e5325daafd5c083e37537a4eda434f988b09a5f Mon Sep 17 00:00:00 2001 From: Javi Date: Sat, 25 Mar 2017 20:39:52 +0100 Subject: [PATCH 001/252] Add dynamic rotations (#2) * Start work on multidynamic rotations * Finish up dynamic rotations :recycle: * Fix typo * Some changes to Dynamic Rotations * Add dynamic player count requirements to config.yml * Fix messages and change participating players count logic --- .../main/i18n/templates/pgm/PGMUI.properties | 3 + PGM/src/main/java/tc/oc/pgm/PGM.java | 2 + .../java/tc/oc/pgm/map/MapModuleContext.java | 7 +++ .../DynamicRotationChangeListener.java | 61 +++++++++++++++++++ .../rotation/FileRotationProviderFactory.java | 3 +- .../tc/oc/pgm/rotation/RotationCategory.java | 5 ++ .../tc/oc/pgm/rotation/RotationManager.java | 4 +- .../oc/pgm/rotation/RotationProviderInfo.java | 4 +- .../tc/oc/pgm/rotation/RotationState.java | 15 +++++ 9 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java create mode 100644 PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties index f53aa6f..c043b56 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties @@ -65,6 +65,9 @@ map.gamemode.long.mixed = Mixed map.gamemode.long.skywars = Skywars map.gamemode.long.survival = Survival Games +rotation.change.broadcast.title = Changing Rotation... +rotation.change.broadcast.info = The server is automatically changing rotations to best accommodate the current player count. + command.match.matchInfo.title = Match command.match.matchInfo.title.tip = View this match on the web command.match.matchInfo.time = Time diff --git a/PGM/src/main/java/tc/oc/pgm/PGM.java b/PGM/src/main/java/tc/oc/pgm/PGM.java index ac56df2..df0c30a 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGM.java +++ b/PGM/src/main/java/tc/oc/pgm/PGM.java @@ -40,6 +40,7 @@ import tc.oc.pgm.match.MatchManager; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.polls.PollListener; import tc.oc.pgm.polls.PollManager; +import tc.oc.pgm.rotation.DynamicRotationChangeListener; import tc.oc.pgm.start.StartCommands; import tc.oc.pgm.tablist.MatchTabManager; import tc.oc.pgm.timelimit.TimeLimitCommands; @@ -199,6 +200,7 @@ public final class PGM extends JavaPlugin { this.registerEvents(new CraftingProtect()); this.registerEvents(new ObjectivesFireworkListener()); this.registerEvents(new ItemTransferListener()); + this.registerEvents(new DynamicRotationChangeListener()); } public void registerEvents(Listener listener) { diff --git a/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java b/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java index 76a991c..d7602f4 100644 --- a/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java +++ b/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java @@ -130,4 +130,11 @@ public class MapModuleContext extends ModuleContext { public Range playerLimits() { return playerLimits.get(); } + + public Integer playerLimitAverage() { + Range lims = playerLimits(); + int sum = lims.lowerEndpoint() + lims.upperEndpoint(); + + return ((Double) Math.floor(sum/2)).intValue(); + } } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java new file mode 100644 index 0000000..a8a4a99 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java @@ -0,0 +1,61 @@ +package tc.oc.pgm.rotation; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.bukkit.configuration.Configuration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import tc.oc.commons.core.chat.Component; +import tc.oc.pgm.Config; +import tc.oc.pgm.PGM; +import tc.oc.pgm.cycle.CycleMatchModule; +import tc.oc.pgm.events.MatchEndEvent; + +public class DynamicRotationChangeListener implements Listener { + + public static void main(String[] args) { + System.out.println(RotationCategory.MEDIUM.toString().toLowerCase()); + } + + @EventHandler + public void onMatchEnd(MatchEndEvent event) { + RotationManager rotationManager = PGM.getMatchManager().getRotationManager(); + + // Ignore if there is only one rotation available + if (rotationManager.getRotations().size() == 1) return; + + // Number of players we can assume is active + int participatingPlayers = event.getMatch().getServer().getOnlinePlayers().size(); + + RotationCategory appr = getAppropriateRotationCategory(participatingPlayers, rotationManager); + if (appr != null && rotationManager.getRotation(appr.toString().toLowerCase()) != rotationManager.getRotation()) { + rotationManager.setRotation(rotationManager.getRotation(appr.toString().toLowerCase())); + CycleMatchModule cmm = event.getMatch().needMatchModule(CycleMatchModule.class); + cmm.startCountdown(cmm.getConfig().countdown()); + + event.getMatch().sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + event.getMatch().sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); + event.getMatch().sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); + event.getMatch().sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + } + } + + /** + * Returns appropriate rotation looking at how many players (participating) are online. + * + * @param players Current participant player count. + * @param rotationManager The {@link RotationManager} + * @return any of {@link RotationCategory} + */ + private RotationCategory getAppropriateRotationCategory(int players, RotationManager rotationManager) { + Configuration config = Config.getConfiguration(); + int medium = config.getInt("rotation.providers.file.medium.count"); + int mega = config.getInt("rotation.providers.file.mega.count"); + + if (players > medium && players <= mega && rotationManager.getRotation("medium") != null) return RotationCategory.MEDIUM; + if (players > mega && rotationManager.getRotation("mega") != null) return RotationCategory.MEGA; + + return RotationCategory.MINI; + } +} diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java index fef4e91..09dfe09 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java @@ -34,9 +34,10 @@ public class FileRotationProviderFactory { if(!rotationFile.isAbsolute()) rotationFile = dataPath.resolve(rotationFile); int priority = provider.getInt("priority", 0); + int count = provider.getInt("count", 0); if(Files.isRegularFile(rotationFile)) { - providers.add(new RotationProviderInfo(new FileRotationProvider(mapLibrary, name, rotationFile, dataPath), name, priority)); + providers.add(new RotationProviderInfo(new FileRotationProvider(mapLibrary, name, rotationFile, dataPath), name, priority, count)); } else if(minecraftService.getLocalServer().startup_visibility() == ServerDoc.Visibility.PUBLIC) { // This is not a perfect way to decide whether or not to throw an error, but it's the best we can do right now mapLibrary.getLogger().severe("Missing rotation file: " + rotationFile); diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java new file mode 100644 index 0000000..3168b1b --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java @@ -0,0 +1,5 @@ +package tc.oc.pgm.rotation; + +public enum RotationCategory { + MINI, MEDIUM, MEGA +} diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java index e2cffb7..284080f 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java @@ -116,12 +116,12 @@ public class RotationManager { return null; } - public void addProvider(@Nonnull RotationProvider provider, @Nonnull String name, int priority) { + public void addProvider(@Nonnull RotationProvider provider, @Nonnull String name, int priority, int count) { Preconditions.checkNotNull(provider, "rotation provider"); Preconditions.checkNotNull(name, "name"); Preconditions.checkArgument(this.getProviderByName(name) == null, "provider is already registered to name"); - RotationProviderInfo state = new RotationProviderInfo(provider, name, priority); + RotationProviderInfo state = new RotationProviderInfo(provider, name, priority, count); this.providers.add(state); } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java index 7a777d3..8983eb8 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java @@ -8,15 +8,17 @@ public class RotationProviderInfo implements Comparable { public final @Nonnull RotationProvider provider; public final int priority; + public final int count; public final @Nonnull String name; - public RotationProviderInfo(@Nonnull RotationProvider provider, @Nonnull String name, int priority) { + public RotationProviderInfo(@Nonnull RotationProvider provider, @Nonnull String name, int priority, int count) { Preconditions.checkNotNull(provider, "rotation provider"); Preconditions.checkNotNull(name, "name"); this.provider = provider; this.name = name; this.priority = priority; + this.count = count; } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java index 0c3b41e..8e8c315 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java @@ -1,5 +1,6 @@ package tc.oc.pgm.rotation; +import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -39,6 +40,20 @@ public final class RotationState { return this.maps; } + /** + * Gets the approximate number of players supposed to be playing the rotation maps. + * @return Integer with average size of teams over all maps + */ + public @Nonnull Integer getAverageNeededPlayers() { + List players = new ArrayList<>(); + int total = 0; + + getMaps().stream().forEach(m -> players.add(m.getContext().playerLimitAverage())); + for (int pl : players) { total += pl; } + + return ((Double) Math.floor(total / players.size())).intValue(); + } + /** * Gets the next map in the rotation as specified by the next id. * @return Next map in rotation From 427515a94bb811a0473effe8f29a0798d1a3c3cb Mon Sep 17 00:00:00 2001 From: RainoBoy97 Date: Sat, 25 Mar 2017 20:40:28 +0100 Subject: [PATCH 002/252] Rename Raindrop(s) to Droplet(s) (#3) --- .../core/src/main/i18n/templates/commons/Commons.properties | 4 ++-- .../src/main/i18n/templates/lobby/LobbyErrors.properties | 4 ++-- .../src/main/i18n/templates/lobby/LobbyMessages.properties | 6 +++--- .../core/src/main/i18n/templates/lobby/LobbyUI.properties | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Commons/core/src/main/i18n/templates/commons/Commons.properties b/Commons/core/src/main/i18n/templates/commons/Commons.properties index d3966ab..1eb5756 100644 --- a/Commons/core/src/main/i18n/templates/commons/Commons.properties +++ b/Commons/core/src/main/i18n/templates/commons/Commons.properties @@ -183,8 +183,8 @@ entity.ThrownEnderpearl.name = Ender Pearl # {2} = "raindrop/raindrops" raindrops.balance.self = You have {1} {2} raindrops.balance.other = {0} has {1} {2} -raindrops.singular = raindrop -raindrops.plural = raindrops +raindrops.singular = droplet +raindrops.plural = droplets material.Iron = Iron material.gold = Gold diff --git a/Commons/core/src/main/i18n/templates/lobby/LobbyErrors.properties b/Commons/core/src/main/i18n/templates/lobby/LobbyErrors.properties index 3c4436f..98d8baa 100644 --- a/Commons/core/src/main/i18n/templates/lobby/LobbyErrors.properties +++ b/Commons/core/src/main/i18n/templates/lobby/LobbyErrors.properties @@ -1,5 +1,5 @@ -gizmo.gun.empty = No Raindrops available to shoot +gizmo.gun.empty = No Droplets available to shoot -raindrops.purchase.fail = You do not have enough Raindrops to purchase this +raindrops.purchase.fail = You do not have enough Droplets to purchase this purchase.purchase.fail = Purchase failed diff --git a/Commons/core/src/main/i18n/templates/lobby/LobbyMessages.properties b/Commons/core/src/main/i18n/templates/lobby/LobbyMessages.properties index 1f9fc3e..eae2b26 100644 --- a/Commons/core/src/main/i18n/templates/lobby/LobbyMessages.properties +++ b/Commons/core/src/main/i18n/templates/lobby/LobbyMessages.properties @@ -7,8 +7,8 @@ gizmo.popper.description = Clear players with a satisfying pop gizmo.rocket.name = Player Rocketer gizmo.rocket.description = Hide players by launching them into colorful fireworks -gizmo.gun.name = Raindrop Gun -gizmo.gun.description = Gift raindrops with a punch :D +gizmo.gun.name = Droplet Gun +gizmo.gun.description = Gift droplets with a punch :D gizmo.chicken.name = Chickenifier5000 gizmo.chicken.description = bok B'GAWK @@ -17,7 +17,7 @@ gizmo.chicken.description = bok B'GAWK # {0} = number of players chickened gizmo.chicken.raindropsResult = chicken'd a total of {0} players -gizmo.gun.raindropsResult = raindrop gun gift +gizmo.gun.raindropsResult = droplet gun gift # {0} = number of players popped gizmo.popper.raindropsResult = popped a total of {0} players diff --git a/Commons/core/src/main/i18n/templates/lobby/LobbyUI.properties b/Commons/core/src/main/i18n/templates/lobby/LobbyUI.properties index 92605b8..d471e6a 100644 --- a/Commons/core/src/main/i18n/templates/lobby/LobbyUI.properties +++ b/Commons/core/src/main/i18n/templates/lobby/LobbyUI.properties @@ -4,10 +4,10 @@ gizmo.current = Current gizmo: {0} gizmo.purchasing = Purchasing... gizmo.purchasing.cancel = Purchase cancelled gizmo.purchased = Purchased -gizmo.purchased.success = Purchased {0} for {1} raindrops +gizmo.purchased.success = Purchased {0} for {1} droplets # {0} The cost in raindrops of the gizmo -gizmo.cost = {0} Raindrops +gizmo.cost = {0} Droplets # {0} the colored name of the gizmo that was equipped gizmo.equip = Gizmo equipped: {0} From a50b29d625b2a78c87e5f9813281c3ecc58ef941 Mon Sep 17 00:00:00 2001 From: javipepe Date: Sun, 26 Mar 2017 14:10:42 +0200 Subject: [PATCH 003/252] Make dynamic rotations work again, add option to disable them in config, and show activation player counts in /rotations --- .../main/i18n/templates/pgm/PGMUI.properties | 1 + .../java/tc/oc/pgm/commands/MapCommands.java | 4 +++- .../DynamicRotationChangeListener.java | 23 +++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties index c043b56..e0a6ba5 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties @@ -19,6 +19,7 @@ command.map.mapInfo.folder = Folder command.map.currentRotation.title = Current Rotation command.map.rotationList.title = Loaded Rotations +command.map.rotationList.activatesWith = (activates with {0} players) map.genre.objectives = Objectives map.genre.deathmatch = Deathmatch diff --git a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java index 37d53e5..b256faf 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -32,6 +32,7 @@ import tc.oc.commons.bukkit.nick.UsernameRenderer; import tc.oc.commons.core.chat.Audience; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.chat.Components; +import tc.oc.pgm.Config; import tc.oc.pgm.PGM; import tc.oc.pgm.PGMTranslations; import tc.oc.pgm.ffa.FreeForAllModule; @@ -238,7 +239,8 @@ public class MapCommands { new PrettyPaginatedResult(PGMTranslations.get().t("command.map.rotationList.title", sender)) { @Override public String format(String rotationName, int index) { - return (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationName; + int activation = Config.getConfiguration().getInt("rotation.providers.file." + rotationName + ".count"); + return (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationName + (activation > 0 ? ChatColor.GRAY + " " + PGMTranslations.get().t("command.map.rotationList.activatesWith", sender, ChatColor.RED + "" + activation + ChatColor.GRAY) : ""); } }.display(new BukkitWrappedCommandSender(sender), Lists.newArrayList(rotations.keySet()), page); } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java index a8a4a99..d6ce58c 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java @@ -11,33 +11,31 @@ import tc.oc.pgm.Config; import tc.oc.pgm.PGM; import tc.oc.pgm.cycle.CycleMatchModule; import tc.oc.pgm.events.MatchEndEvent; +import tc.oc.pgm.match.Match; public class DynamicRotationChangeListener implements Listener { - public static void main(String[] args) { - System.out.println(RotationCategory.MEDIUM.toString().toLowerCase()); - } @EventHandler public void onMatchEnd(MatchEndEvent event) { RotationManager rotationManager = PGM.getMatchManager().getRotationManager(); // Ignore if there is only one rotation available - if (rotationManager.getRotations().size() == 1) return; + if (rotationManager.getRotations().size() == 1 || !Config.getConfiguration().getBoolean("rotation.dynamic")) return; // Number of players we can assume is active int participatingPlayers = event.getMatch().getServer().getOnlinePlayers().size(); + // Get appropriate rotation RotationCategory appr = getAppropriateRotationCategory(participatingPlayers, rotationManager); - if (appr != null && rotationManager.getRotation(appr.toString().toLowerCase()) != rotationManager.getRotation()) { + + if (appr != null && !rotationManager.getCurrentRotationName().equals(appr.toString().toLowerCase())) { rotationManager.setRotation(rotationManager.getRotation(appr.toString().toLowerCase())); CycleMatchModule cmm = event.getMatch().needMatchModule(CycleMatchModule.class); cmm.startCountdown(cmm.getConfig().countdown()); - event.getMatch().sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); - event.getMatch().sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); - event.getMatch().sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); - event.getMatch().sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + PGM.get().getLogger().info("[Dynamic Rotations] Changing to \"" + appr.toString().toLowerCase() + "\" rotation..."); + sendRotationChangeMessage(event.getMatch()); } } @@ -58,4 +56,11 @@ public class DynamicRotationChangeListener implements Listener { return RotationCategory.MINI; } + + private void sendRotationChangeMessage(Match match) { + match.sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); + match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); + match.sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + } } From 345b6cde423d5b9a41a7be2cfdd2f67ff50b3ca4 Mon Sep 17 00:00:00 2001 From: javipepe Date: Sun, 26 Mar 2017 14:58:56 +0200 Subject: [PATCH 004/252] Some clean-up on dynamic rotations code :sparkles: --- .../pgm/rotation/DynamicRotationChangeListener.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java index d6ce58c..2f61045 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java @@ -6,6 +6,7 @@ import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.configuration.Configuration; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import tc.oc.commons.bukkit.chat.HeaderComponent; import tc.oc.commons.core.chat.Component; import tc.oc.pgm.Config; import tc.oc.pgm.PGM; @@ -20,14 +21,14 @@ public class DynamicRotationChangeListener implements Listener { public void onMatchEnd(MatchEndEvent event) { RotationManager rotationManager = PGM.getMatchManager().getRotationManager(); - // Ignore if there is only one rotation available - if (rotationManager.getRotations().size() == 1 || !Config.getConfiguration().getBoolean("rotation.dynamic")) return; + // Ignore if dynamic rotations are disabled or if there is only one rotation available + if (!Config.getConfiguration().getBoolean("rotation.dynamic") || rotationManager.getRotations().size() == 1) return; // Number of players we can assume is active - int participatingPlayers = event.getMatch().getServer().getOnlinePlayers().size(); + int playersOnline = event.getMatch().getServer().getOnlinePlayers().size(); // Get appropriate rotation - RotationCategory appr = getAppropriateRotationCategory(participatingPlayers, rotationManager); + RotationCategory appr = getAppropriateRotationCategory(playersOnline, rotationManager); if (appr != null && !rotationManager.getCurrentRotationName().equals(appr.toString().toLowerCase())) { rotationManager.setRotation(rotationManager.getRotation(appr.toString().toLowerCase())); @@ -58,9 +59,9 @@ public class DynamicRotationChangeListener implements Listener { } private void sendRotationChangeMessage(Match match) { - match.sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + match.sendMessage(new HeaderComponent(ChatColor.RED)); match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); - match.sendMessage(new TextComponent(ChatColor.RED + "" + ChatColor.STRIKETHROUGH + "---------------------------------------------------")); + match.sendMessage(new HeaderComponent(ChatColor.RED)); } } From 5ebcc760c9714358cc64f611d0c9410f7d6228ea Mon Sep 17 00:00:00 2001 From: javipepe Date: Sun, 26 Mar 2017 19:49:14 +0200 Subject: [PATCH 005/252] Enchance rotation commands a bit --- PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java | 6 ++++-- .../tc/oc/pgm/rotation/DynamicRotationChangeListener.java | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java index b256faf..934c809 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -215,7 +215,7 @@ public class MapCommands { final RotationState rotation = CommandUtils.getRotation(args.getFlag('n'), sender); int page = args.getInteger(0, 1); - new PrettyPaginatedResult(PGMTranslations.get().t("command.map.currentRotation.title", sender)) { + new PrettyPaginatedResult(PGMTranslations.get().t("command.map.currentRotation.title", sender) + "(" + ChatColor.DARK_AQUA + PGM.getMatchManager().getRotationManager().getCurrentRotationName() + ChatColor.RESET + ")") { @Override public String format(PGMMap map, int index) { ChatColor color = index == rotation.getNextId() ? ChatColor.DARK_AQUA : ChatColor.WHITE; return color.toString() + (index + 1) + ". " + map.getInfo().getShortDescription(sender); @@ -240,7 +240,9 @@ public class MapCommands { new PrettyPaginatedResult(PGMTranslations.get().t("command.map.rotationList.title", sender)) { @Override public String format(String rotationName, int index) { int activation = Config.getConfiguration().getInt("rotation.providers.file." + rotationName + ".count"); - return (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationName + (activation > 0 ? ChatColor.GRAY + " " + PGMTranslations.get().t("command.map.rotationList.activatesWith", sender, ChatColor.RED + "" + activation + ChatColor.GRAY) : ""); + boolean current = rotationName.equalsIgnoreCase(PGM.getMatchManager().getRotationManager().getCurrentRotationName()); + + return (current ? ChatColor.GOLD + "\u0187 " : "") + (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationName + (activation > 0 ? ChatColor.GRAY + " " + PGMTranslations.get().t("command.map.rotationList.activatesWith", sender, ChatColor.RED + "" + activation + ChatColor.GRAY) : ""); } }.display(new BukkitWrappedCommandSender(sender), Lists.newArrayList(rotations.keySet()), page); } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java index 2f61045..841763f 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java @@ -31,7 +31,8 @@ public class DynamicRotationChangeListener implements Listener { RotationCategory appr = getAppropriateRotationCategory(playersOnline, rotationManager); if (appr != null && !rotationManager.getCurrentRotationName().equals(appr.toString().toLowerCase())) { - rotationManager.setRotation(rotationManager.getRotation(appr.toString().toLowerCase())); + rotationManager.setRotation(appr.toString().toLowerCase(), rotationManager.getRotation(appr.toString().toLowerCase())); + rotationManager.setCurrentRotationName(appr.toString().toLowerCase()); CycleMatchModule cmm = event.getMatch().needMatchModule(CycleMatchModule.class); cmm.startCountdown(cmm.getConfig().countdown()); From 98965c16d2b78b7554a85949b9199e6ae57b9217 Mon Sep 17 00:00:00 2001 From: Pablete1234 Date: Mon, 27 Mar 2017 05:49:12 +0200 Subject: [PATCH 006/252] Fix dynamic rotations, make use of guice --- PGM/src/main/java/tc/oc/pgm/PGM.java | 2 - PGM/src/main/java/tc/oc/pgm/PGMManifest.java | 3 + .../java/tc/oc/pgm/commands/MapCommands.java | 27 +++---- .../java/tc/oc/pgm/map/MapModuleContext.java | 4 +- .../DynamicRotationChangeListener.java | 68 ----------------- .../pgm/rotation/DynamicRotationListener.java | 73 +++++++++++++++++++ .../tc/oc/pgm/rotation/RotationCategory.java | 5 -- .../tc/oc/pgm/rotation/RotationManager.java | 4 + .../oc/pgm/rotation/RotationProviderInfo.java | 11 +++ .../tc/oc/pgm/rotation/RotationState.java | 13 ++-- PGM/src/main/resources/config.yml | 1 + 11 files changed, 111 insertions(+), 100 deletions(-) delete mode 100644 PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java create mode 100644 PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java diff --git a/PGM/src/main/java/tc/oc/pgm/PGM.java b/PGM/src/main/java/tc/oc/pgm/PGM.java index df0c30a..ac56df2 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGM.java +++ b/PGM/src/main/java/tc/oc/pgm/PGM.java @@ -40,7 +40,6 @@ import tc.oc.pgm.match.MatchManager; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.polls.PollListener; import tc.oc.pgm.polls.PollManager; -import tc.oc.pgm.rotation.DynamicRotationChangeListener; import tc.oc.pgm.start.StartCommands; import tc.oc.pgm.tablist.MatchTabManager; import tc.oc.pgm.timelimit.TimeLimitCommands; @@ -200,7 +199,6 @@ public final class PGM extends JavaPlugin { this.registerEvents(new CraftingProtect()); this.registerEvents(new ObjectivesFireworkListener()); this.registerEvents(new ItemTransferListener()); - this.registerEvents(new DynamicRotationChangeListener()); } public void registerEvents(Listener listener) { diff --git a/PGM/src/main/java/tc/oc/pgm/PGMManifest.java b/PGM/src/main/java/tc/oc/pgm/PGMManifest.java index 6e36213..0d61886 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGMManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/PGMManifest.java @@ -4,6 +4,7 @@ import tc.oc.commons.bukkit.chat.FlairRenderer; import tc.oc.commons.bukkit.nick.UsernameRenderer; import tc.oc.commons.core.inject.HybridManifest; import tc.oc.commons.core.plugin.PluginFacetBinder; +import tc.oc.minecraft.api.event.ListenerBinder; import tc.oc.pgm.analytics.MatchAnalyticsManifest; import tc.oc.pgm.antigrief.DefuseListener; import tc.oc.pgm.chat.MatchFlairRenderer; @@ -33,6 +34,7 @@ import tc.oc.pgm.match.MatchPlayerEventRouter; import tc.oc.pgm.module.MatchModulesManifest; import tc.oc.pgm.mutation.command.MutationCommands; import tc.oc.pgm.restart.RestartListener; +import tc.oc.pgm.rotation.DynamicRotationListener; import tc.oc.pgm.settings.Settings; import tc.oc.pgm.spawns.states.State; import tc.oc.pgm.tnt.license.LicenseBroker; @@ -65,6 +67,7 @@ public final class PGMManifest extends HybridManifest { bind(MapLibrary.class).to(MapLibraryImpl.class); bind(MapLoader.class).to(MapLoaderImpl.class); + new ListenerBinder(binder()).bindListener().to(DynamicRotationListener.class); // Tourney needs this expose(MapLibrary.class); diff --git a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java index 934c809..3fc0510 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -2,14 +2,11 @@ package tc.oc.pgm.commands; import java.net.URL; import java.util.List; -import java.util.Map; import java.util.Set; import javax.annotation.Nullable; -import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Lists; import com.sk89q.bukkit.util.BukkitWrappedCommandSender; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; @@ -32,20 +29,17 @@ import tc.oc.commons.bukkit.nick.UsernameRenderer; import tc.oc.commons.core.chat.Audience; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.chat.Components; -import tc.oc.pgm.Config; import tc.oc.pgm.PGM; import tc.oc.pgm.PGMTranslations; import tc.oc.pgm.ffa.FreeForAllModule; import tc.oc.pgm.map.Contributor; import tc.oc.pgm.map.MapInfo; import tc.oc.pgm.map.PGMMap; -import tc.oc.pgm.match.MatchManager; -import tc.oc.pgm.match.Party; import tc.oc.pgm.modules.InfoModule; import tc.oc.pgm.rotation.RotationManager; +import tc.oc.pgm.rotation.RotationProviderInfo; import tc.oc.pgm.rotation.RotationState; import tc.oc.pgm.teams.TeamFactory; -import tc.oc.pgm.teams.TeamModule; public class MapCommands { @Command( @@ -234,17 +228,20 @@ public class MapCommands { @CommandPermissions("pgm.rotation.list") public static void rotations(final CommandContext args, final CommandSender sender) throws CommandException { RotationManager manager = PGM.getMatchManager().getRotationManager(); - Map rotations = manager.getRotations(); + List rotations = manager.getProviders(); int page = args.getFlagInteger('p', 1); - new PrettyPaginatedResult(PGMTranslations.get().t("command.map.rotationList.title", sender)) { - @Override public String format(String rotationName, int index) { - int activation = Config.getConfiguration().getInt("rotation.providers.file." + rotationName + ".count"); - boolean current = rotationName.equalsIgnoreCase(PGM.getMatchManager().getRotationManager().getCurrentRotationName()); - - return (current ? ChatColor.GOLD + "\u0187 " : "") + (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationName + (activation > 0 ? ChatColor.GRAY + " " + PGMTranslations.get().t("command.map.rotationList.activatesWith", sender, ChatColor.RED + "" + activation + ChatColor.GRAY) : ""); + new PrettyPaginatedResult(PGMTranslations.get().t("command.map.rotationList.title", sender)) { + @Override public String format(RotationProviderInfo rotationInfo, int index) { + boolean current = manager.getCurrentRotationName().equals(rotationInfo.name); + int count = rotationInfo.count; + return (current ? ChatColor.GOLD : ChatColor.GRAY) + " \u25ba " + + (index % 2 == 0 ? ChatColor.AQUA : ChatColor.DARK_AQUA) + rotationInfo.name + + (count > 0 ? ChatColor.GRAY + " " + + PGMTranslations.get().t("command.map.rotationList.activatesWith", sender, + ChatColor.RED + "" + count + ChatColor.GRAY) : ""); } - }.display(new BukkitWrappedCommandSender(sender), Lists.newArrayList(rotations.keySet()), page); + }.display(new BukkitWrappedCommandSender(sender), rotations, page); } @Command( diff --git a/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java b/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java index d7602f4..4f75133 100644 --- a/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java +++ b/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java @@ -133,8 +133,6 @@ public class MapModuleContext extends ModuleContext { public Integer playerLimitAverage() { Range lims = playerLimits(); - int sum = lims.lowerEndpoint() + lims.upperEndpoint(); - - return ((Double) Math.floor(sum/2)).intValue(); + return (lims.lowerEndpoint() + lims.upperEndpoint()) / 2; } } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java deleted file mode 100644 index 841763f..0000000 --- a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationChangeListener.java +++ /dev/null @@ -1,68 +0,0 @@ -package tc.oc.pgm.rotation; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.chat.TranslatableComponent; -import org.bukkit.configuration.Configuration; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import tc.oc.commons.bukkit.chat.HeaderComponent; -import tc.oc.commons.core.chat.Component; -import tc.oc.pgm.Config; -import tc.oc.pgm.PGM; -import tc.oc.pgm.cycle.CycleMatchModule; -import tc.oc.pgm.events.MatchEndEvent; -import tc.oc.pgm.match.Match; - -public class DynamicRotationChangeListener implements Listener { - - - @EventHandler - public void onMatchEnd(MatchEndEvent event) { - RotationManager rotationManager = PGM.getMatchManager().getRotationManager(); - - // Ignore if dynamic rotations are disabled or if there is only one rotation available - if (!Config.getConfiguration().getBoolean("rotation.dynamic") || rotationManager.getRotations().size() == 1) return; - - // Number of players we can assume is active - int playersOnline = event.getMatch().getServer().getOnlinePlayers().size(); - - // Get appropriate rotation - RotationCategory appr = getAppropriateRotationCategory(playersOnline, rotationManager); - - if (appr != null && !rotationManager.getCurrentRotationName().equals(appr.toString().toLowerCase())) { - rotationManager.setRotation(appr.toString().toLowerCase(), rotationManager.getRotation(appr.toString().toLowerCase())); - rotationManager.setCurrentRotationName(appr.toString().toLowerCase()); - CycleMatchModule cmm = event.getMatch().needMatchModule(CycleMatchModule.class); - cmm.startCountdown(cmm.getConfig().countdown()); - - PGM.get().getLogger().info("[Dynamic Rotations] Changing to \"" + appr.toString().toLowerCase() + "\" rotation..."); - sendRotationChangeMessage(event.getMatch()); - } - } - - /** - * Returns appropriate rotation looking at how many players (participating) are online. - * - * @param players Current participant player count. - * @param rotationManager The {@link RotationManager} - * @return any of {@link RotationCategory} - */ - private RotationCategory getAppropriateRotationCategory(int players, RotationManager rotationManager) { - Configuration config = Config.getConfiguration(); - int medium = config.getInt("rotation.providers.file.medium.count"); - int mega = config.getInt("rotation.providers.file.mega.count"); - - if (players > medium && players <= mega && rotationManager.getRotation("medium") != null) return RotationCategory.MEDIUM; - if (players > mega && rotationManager.getRotation("mega") != null) return RotationCategory.MEGA; - - return RotationCategory.MINI; - } - - private void sendRotationChangeMessage(Match match) { - match.sendMessage(new HeaderComponent(ChatColor.RED)); - match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); - match.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); - match.sendMessage(new HeaderComponent(ChatColor.RED)); - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java new file mode 100644 index 0000000..ae76f8b --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java @@ -0,0 +1,73 @@ +package tc.oc.pgm.rotation; + +import com.google.inject.Inject; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import tc.oc.api.bukkit.users.OnlinePlayers; +import tc.oc.commons.bukkit.chat.Audiences; +import tc.oc.commons.bukkit.chat.HeaderComponent; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.logging.Loggers; +import tc.oc.commons.core.plugin.PluginFacet; +import tc.oc.pgm.PGM; +import tc.oc.pgm.events.MatchEndEvent; + +import java.util.logging.Logger; + +public class DynamicRotationListener implements PluginFacet, Listener { + + private final Logger logger; + private final Audiences audiences; + private final OnlinePlayers players; + private final ConfigurationSection config; + + @Inject DynamicRotationListener(Loggers loggers, Audiences audiences, OnlinePlayers players, Configuration config) { + this.logger = loggers.get(getClass()); + this.audiences = audiences; + this.players = players; + this.config = config.getConfigurationSection("rotation"); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMatchEnd(MatchEndEvent event) { + RotationManager rotationManager = PGM.getMatchManager().getRotationManager(); + + // Ignore if dynamic rotations are disabled or if there is only one rotation available + if (!config.getBoolean("dynamic", false) || rotationManager.getRotations().size() <= 1) return; + + int playerCount = players.count(); + + // Get appropriate rotation + RotationProviderInfo rotation = rotationManager.getProviders().stream() + .filter(info -> playerCount >= info.count).findFirst().orElse(null); + + if (rotation == null) { + logger.warning("No valid rotation was found to accommodate " + playerCount + " players. Missing fallback?"); + } else { + RotationState rotState = rotation.provider.getRotation(rotation.name); + + if (rotState == null) { + logger.warning("'" + rotation.name + "' rotation provider doesn't have a rotation with it's own name"); + } else if (!rotationManager.getCurrentRotationName().equals(rotation.name)) { + rotationManager.setRotation(rotation.name, rotState); + rotationManager.setCurrentRotationName(rotation.name); + + logger.info("Changing to \"" + rotation.name + "\" rotation..."); + sendRotationChangeMessage(audiences.localServer()); + } + } + } + + private void sendRotationChangeMessage(Audience audience) { + audience.sendMessage(new HeaderComponent(ChatColor.RED)); + audience.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.title"), ChatColor.GOLD)); + audience.sendMessage(new Component(new TranslatableComponent("rotation.change.broadcast.info"), ChatColor.YELLOW)); + audience.sendMessage(new HeaderComponent(ChatColor.RED)); + } +} diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java deleted file mode 100644 index 3168b1b..0000000 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationCategory.java +++ /dev/null @@ -1,5 +0,0 @@ -package tc.oc.pgm.rotation; - -public enum RotationCategory { - MINI, MEDIUM, MEGA -} diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java index 284080f..1ed6ec6 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java @@ -115,6 +115,10 @@ public class RotationManager { } return null; } + + public @Nonnull List getProviders() { + return ImmutableList.copyOf(this.providers); + } public void addProvider(@Nonnull RotationProvider provider, @Nonnull String name, int priority, int count) { Preconditions.checkNotNull(provider, "rotation provider"); diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java index 8983eb8..c9333f2 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationProviderInfo.java @@ -29,4 +29,15 @@ public class RotationProviderInfo implements Comparable { } return c; } + + @Override + public String toString() { + return "RotationProviderInfo{" + + "provider=" + provider.getRotations() + + ", priority=" + priority + + ", count=" + count + + ", name='" + name + '\'' + + '}'; + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java index 8e8c315..cbea67d 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java @@ -5,6 +5,7 @@ import java.util.List; import javax.annotation.Nonnull; +import tc.oc.commons.core.util.Lazy; import tc.oc.pgm.map.PGMMap; import com.google.common.base.Preconditions; @@ -40,18 +41,16 @@ public final class RotationState { return this.maps; } + + private Lazy averageNeededPlayers = Lazy.from(() -> + (int) getMaps().stream().mapToInt(map -> map.getContext().playerLimitAverage()).average().orElse(0)); + /** * Gets the approximate number of players supposed to be playing the rotation maps. * @return Integer with average size of teams over all maps */ public @Nonnull Integer getAverageNeededPlayers() { - List players = new ArrayList<>(); - int total = 0; - - getMaps().stream().forEach(m -> players.add(m.getContext().playerLimitAverage())); - for (int pl : players) { total += pl; } - - return ((Double) Math.floor(total / players.size())).intValue(); + return averageNeededPlayers.get(); } /** diff --git a/PGM/src/main/resources/config.yml b/PGM/src/main/resources/config.yml index 21727da..57dafbe 100644 --- a/PGM/src/main/resources/config.yml +++ b/PGM/src/main/resources/config.yml @@ -79,6 +79,7 @@ environment: ranked: false rotation: + dynamic: false default-name: default initial-wait: 20000 providers: {} From daecc6a82ca2ad26630064d56b4c6a9139f64d1e Mon Sep 17 00:00:00 2001 From: Pablete1234 Date: Mon, 27 Mar 2017 06:51:52 +0200 Subject: [PATCH 007/252] Enable block 36 and add proto 1.4.3 --- .../listeners/WorldProblemMatchModule.java | 21 ++++++++++++------- .../java/tc/oc/pgm/map/ProtoVersions.java | 5 ++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/PGM/src/main/java/tc/oc/pgm/listeners/WorldProblemMatchModule.java b/PGM/src/main/java/tc/oc/pgm/listeners/WorldProblemMatchModule.java index 8665339..e8eb018 100644 --- a/PGM/src/main/java/tc/oc/pgm/listeners/WorldProblemMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/listeners/WorldProblemMatchModule.java @@ -19,11 +19,14 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; +import tc.oc.api.docs.SemanticVersion; import tc.oc.api.util.Permissions; import tc.oc.commons.bukkit.util.BlockVectorSet; import tc.oc.commons.bukkit.util.ChunkPosition; import tc.oc.commons.bukkit.util.NMSHacks; import tc.oc.pgm.events.ListenerScope; +import tc.oc.pgm.map.MapProto; +import tc.oc.pgm.map.ProtoVersions; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchScope; @@ -35,8 +38,9 @@ public class WorldProblemMatchModule extends MatchModule implements Listener { private final Set repairedChunks = new HashSet<>(); private final BlockVectorSet block36Locations = new BlockVectorSet(); - - private @Inject World world; + + @Inject private @MapProto SemanticVersion proto; + @Inject private World world; @Inject WorldProblemMatchModule(Match match) { super(match); @@ -99,13 +103,14 @@ public class WorldProblemMatchModule extends MatchModule implements Listener { ironDoor.setType(Material.BARRIER, false); } } - - // Remove all block 36 and remember the ones at y=0 so VoidFilter can check them - for(Block block36 : chunk.getBlocks(Material.PISTON_MOVING_PIECE)) { - if(block36.getY() == 0) { - block36Locations.add(block36.getX(), block36.getY(), block36.getZ()); + if (proto.isOlderThan(ProtoVersions.ENABLE_BLOCK_36)) { + // Remove all block 36 and remember the ones at y=0 so VoidFilter can check them + for(Block block36 : chunk.getBlocks(Material.PISTON_MOVING_PIECE)) { + if(block36.getY() == 0) { + block36Locations.add(block36.getX(), block36.getY(), block36.getZ()); + } + block36.setType(Material.AIR, false); } - block36.setType(Material.AIR, false); } } } diff --git a/PGM/src/main/java/tc/oc/pgm/map/ProtoVersions.java b/PGM/src/main/java/tc/oc/pgm/map/ProtoVersions.java index bdd035c..62ca0e1 100644 --- a/PGM/src/main/java/tc/oc/pgm/map/ProtoVersions.java +++ b/PGM/src/main/java/tc/oc/pgm/map/ProtoVersions.java @@ -41,7 +41,10 @@ public class ProtoVersions { // Various changes to support dynamic filters public static final SemanticVersion DYNAMIC_FILTERS = new SemanticVersion(1, 4, 2); + + // Enable block 36 to be used, protos before this will remove it from the map + public static final SemanticVersion ENABLE_BLOCK_36 = new SemanticVersion(1, 4, 3); // Current version - change this whenever we bump the proto - public static final SemanticVersion CURRENT = DYNAMIC_FILTERS; + public static final SemanticVersion CURRENT = ENABLE_BLOCK_36; } From 91fb5b3fe693925c111c8b1cff387abddf549387 Mon Sep 17 00:00:00 2001 From: Electroid Date: Fri, 31 Mar 2017 14:30:41 -0700 Subject: [PATCH 008/252] Add new mutations --- .../java/tc/oc/api/docs/virtual/MatchDoc.java | 6 +- .../tc/oc/api/docs/virtual/ServerDoc.java | 2 +- .../serialization/TypeAdaptersManifest.java | 2 - .../servers/LocalServerDocument.java | 2 +- .../i18n/templates/pgm/PGMMessages.properties | 65 ++-- .../java/tc/oc/pgm/api/MatchDocument.java | 6 +- .../tc/oc/pgm/blitz/BlitzMatchModule.java | 2 +- .../pgm/damage/DisableDamageMatchModule.java | 17 +- .../tc/oc/pgm/doublejump/DoubleJumpKit.java | 17 +- .../effect/ProjectileTrailMatchModule.java | 2 +- .../matcher/match/MatchMutationFilter.java | 2 +- .../java/tc/oc/pgm/gamerules/GameRule.java | 30 -- .../pgm/gamerules/GameRulesMatchModule.java | 33 +- .../tc/oc/pgm/gamerules/GameRulesModule.java | 22 +- .../java/tc/oc/pgm/killreward/KillReward.java | 6 + .../pgm/killreward/KillRewardMatchModule.java | 40 ++- .../oc/pgm/killreward/KillRewardModule.java | 14 +- .../main/java/tc/oc/pgm/kits/FreeItemKit.java | 39 +++ .../main/java/tc/oc/pgm/kits/HealthKit.java | 2 - .../oc/pgm/kits/NaturalRegenerationKit.java | 19 +- .../main/java/tc/oc/pgm/kits/SlotItemKit.java | 1 + .../tc/oc/pgm/listeners/MatchAnnouncer.java | 4 +- .../java/tc/oc/pgm/listeners/PGMListener.java | 13 +- .../tc/oc/pgm/modules/MobsMatchModule.java | 9 +- .../java/tc/oc/pgm/mutation/Mutation.java | 93 +++--- .../oc/pgm/mutation/MutationMatchModule.java | 89 ++--- .../tc/oc/pgm/mutation/MutationQueue.java | 8 +- .../mutation/command/MutationCommands.java | 91 +++-- .../mutation/submodule/KitMutationModule.java | 28 -- .../mutation/submodule/MutationModule.java | 68 ---- .../mutation/submodule/MutationModules.java | 110 ------ .../submodule/TargetableMutationModule.java | 46 --- .../tc/oc/pgm/mutation/types/KitMutation.java | 111 ++++++ .../oc/pgm/mutation/types/MutationModule.java | 96 ++++++ .../oc/pgm/mutation/types/TargetMutation.java | 64 ++++ .../pgm/mutation/types/kit/ArmorMutation.java | 78 +++++ .../mutation/types/kit/ElytraMutation.java | 42 +++ .../types/kit/EnchantmentMutation.java | 159 +++++++++ .../types/kit/EquestrianMutation.java | 150 +++++++++ .../mutation/types/kit/ExplosiveMutation.java | 70 ++++ .../pgm/mutation/types/kit/GlowMutation.java | 17 + .../mutation/types/kit/HardcoreMutation.java | 48 +++ .../mutation/types/kit/HealthMutation.java | 24 ++ .../pgm/mutation/types/kit/JumpMutation.java | 14 + .../pgm/mutation/types/kit/MobsMutation.java | 56 ++++ .../mutation/types/kit/NoFallMutation.java | 44 +++ .../mutation/types/kit/PotionMutation.java | 100 ++++++ .../types/kit/ProjectileMutation.java | 84 +++++ .../mutation/types/kit/StealthMutation.java | 25 ++ .../mutation/types/other/RageMutation.java | 29 ++ .../types/targetable/ApocalypseMutation.java | 315 ++++++++++++++++++ .../types/targetable/BomberMutation.java | 66 ++++ .../types/targetable/LightningMutation.java | 42 +++ .../tc/oc/pgm/shield/ShieldParameters.java | 4 + .../java/tc/oc/pgm/spawns/states/Alive.java | 17 +- .../java/tc/oc/pgm/mutation/MutationTest.java | 21 -- .../oc/commons/bukkit/item/ItemBuilder.java | 12 +- .../tc/oc/commons/bukkit/item/ItemUtils.java | 19 ++ .../core/collection/ForwardingSet.java | 91 +++++ .../commons/core/collection/WeakHashSet.java | 17 + .../tc/oc/commons/core/random/Entropy.java | 5 + .../oc/commons/core/random/RandomUtils.java | 5 + 62 files changed, 2171 insertions(+), 542 deletions(-) delete mode 100644 PGM/src/main/java/tc/oc/pgm/gamerules/GameRule.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/submodule/KitMutationModule.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModule.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModules.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/submodule/TargetableMutationModule.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ArmorMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/GlowMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/JumpMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/kit/StealthMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java delete mode 100644 PGM/src/test/java/tc/oc/pgm/mutation/MutationTest.java create mode 100644 Util/core/src/main/java/tc/oc/commons/core/collection/ForwardingSet.java create mode 100644 Util/core/src/main/java/tc/oc/commons/core/collection/WeakHashSet.java diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/MatchDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/MatchDoc.java index 01aea88..f52da24 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/MatchDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/MatchDoc.java @@ -30,11 +30,7 @@ public interface MatchDoc extends Model { Collection winning_team_ids(); Collection winning_user_ids(); - enum Mutation { - BLITZ, UHC, EXPLOSIVES, NO_FALL, MOBS, STRENGTH, DOUBLE_JUMP, INVISIBILITY, LIGHTNING, RAGE, ELYTRA; - } - - Set mutations(); + Set mutations(); @Serialize interface Team extends MapDoc.Team, CompetitorDoc { diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java index c646e22..840dc6c 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java @@ -138,7 +138,7 @@ public interface ServerDoc { @Serialize interface Mutation extends Partial { - Set queued_mutations(); + Set queued_mutations(); } /** diff --git a/API/api/src/main/java/tc/oc/api/serialization/TypeAdaptersManifest.java b/API/api/src/main/java/tc/oc/api/serialization/TypeAdaptersManifest.java index 6418b96..f8ff35b 100644 --- a/API/api/src/main/java/tc/oc/api/serialization/TypeAdaptersManifest.java +++ b/API/api/src/main/java/tc/oc/api/serialization/TypeAdaptersManifest.java @@ -33,7 +33,5 @@ public class TypeAdaptersManifest extends Manifest { gson.bindAdapter(new TypeLiteral>(){}) .to(new TypeLiteral>(){}); - gson.bindAdapter(new TypeLiteral>(){}) - .to(new TypeLiteral>(){}); } } diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java index d3b8eca..64498ad 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java @@ -279,7 +279,7 @@ public class LocalServerDocument extends StartupServerDocument implements Server } @Override - public Set queued_mutations() { + public Set queued_mutations() { return mutations != null ? mutations.queued_mutations() : Collections.emptySet(); } } diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index edec828..e509e6a 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -90,9 +90,19 @@ command.mutation.enable.later.plural = You have enabled the {0} mutations for th command.mutation.disable.later.singular = You have disabled the {0} mutation for the next match command.mutation.disable.later.plural = You have disabled the {0} mutations for the next match +# {0} = command sender +# {1} = mutation name(s) +command.mutation.enable.now.singular = {0} has enabled the {1} mutation +command.mutation.enable.now.plural = {0} has enabled the {1} mutations +command.mutation.disable.now.singular = {0} has disabled the {1} mutation +command.mutation.disable.now.plural = {0} has disabled the {1} mutations + +command.mutation.error.mutate = An internal error occured with the '{0}' mutation command.mutation.error.find = Unable to find mutation named '{0}' -command.mutation.error.enabled = All mutations have already been enabled -command.mutation.error.disabled = All mutations have already been disabled +command.mutation.error.enabled = {0} mutation is already enabled +command.mutation.error.enabled.all = All mutations have already been enabled +command.mutation.error.disabled = {0} mutation is already disabled +command.mutation.error.disabled.all = All mutations have already been disabled command.mutation.list.current = Current Mutations command.mutation.list.queued = Queued Mutations @@ -177,28 +187,41 @@ skillRequirement.fail.general = Play on unranked servers to improve your skill a huddle.instructions = Your team now has {0} to strategize before the match starts mutation.type.blitz = Blitz -mutation.type.uhc = UHC -mutation.type.explosives = Explosives -mutation.type.no_fall = No Fall -mutation.type.mobs = Mobs -mutation.type.strength = Strength -mutation.type.double_jump = Double Jump -mutation.type.invisibility = Invisibility -mutation.type.lightning = Lightning -mutation.type.rage = Rage -mutation.type.elytra = Elytra - mutation.type.blitz.desc = no respawning -mutation.type.uhc.desc = no natural regeneration -mutation.type.explosives.desc = stronger and more powerful explosions -mutation.type.no_fall.desc = fall all you want -mutation.type.mobs.desc = natural mob spawning -mutation.type.strength.desc = strength potions everywhere -mutation.type.double_jump.desc = super jump powers -mutation.type.invisibility.desc = enemy players cannot be seen -mutation.type.lightning.desc = lightning strikes from the sky +mutation.type.rage = Rage mutation.type.rage.desc = instant kills +mutation.type.hardcore = Hardcore +mutation.type.hardcore.desc = no natural regeneration +mutation.type.jump = Jump +mutation.type.jump.desc = double jump and no fall +mutation.type.explosive = Explosive +mutation.type.explosive.desc = tnt and fire bows +mutation.type.elytra = Elytra mutation.type.elytra.desc = fly around with an elytra +mutation.type.projectile = Projectile +mutation.type.projectile.desc = arrow potion effects +mutation.type.enchantment = Enchantment +mutation.type.enchantment.desc = random item enchantments +mutation.type.potion = Potion +mutation.type.potion.desc = random special potions +mutation.type.equestrian = Equestrian +mutation.type.equestrian.desc = ride a custom horse +mutation.type.health = Health +mutation.type.health.desc = double health and gold apples +mutation.type.glow = Glow +mutation.type.glow.desc = glowing effect on all players +mutation.type.stealth = Stealth +mutation.type.stealth.desc = invisibility with no armor +mutation.type.armor = Armor +mutation.type.armor.desc = heavy diamond armor +mutation.type.mobs = Mobs +mutation.type.mobs.desc = mob spawning eggs +mutation.type.lightning = Lightning +mutation.type.lightning.desc = lightning strikes from the sky +mutation.type.bomber = Bomber +mutation.type.bomber.desc = tnt rain +mutation.type.apocalypse = Apocalypse +mutation.type.apocalypse.desc = mob spawning chaos tnt.license.info.alreadyHas = You have a TNT license. You can surrender your license by typing {0} tnt.license.info.doesNotHave = You do not have a TNT license. You can request one by typing {0} diff --git a/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java b/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java index 6903a88..5254722 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java +++ b/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java @@ -120,10 +120,10 @@ public class MatchDocument extends AbstractModel implements MatchDoc { } @Override - public Set mutations() { - return mutations.map(mmm -> mmm.getHistoricalMutations() + public Set mutations() { + return mutations.map(mmm -> mmm.mutationsHistorical() .stream() - .map(tc.oc.pgm.mutation.Mutation::toApi) + .map(tc.oc.pgm.mutation.Mutation::name) .collect(Collectors.toImmutableSet())) .orElse(ImmutableSet.of()); } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java index 76cc0c1..32a461f 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java @@ -65,7 +65,7 @@ public class BlitzMatchModule extends MatchModule implements Listener, JoinHandl public BlitzMatchModule(Match match, BlitzConfig config) { super(match); - this.config = MutationMatchModule.check(match, Mutation.BLITZ) ? new BlitzConfig(1, true) : config; + this.config = match.module(MutationMatchModule.class).get().enabled(Mutation.BLITZ) ? new BlitzConfig(1, true) : config; this.lifeManager = new LifeManager(this.config.getNumLives()); } diff --git a/PGM/src/main/java/tc/oc/pgm/damage/DisableDamageMatchModule.java b/PGM/src/main/java/tc/oc/pgm/damage/DisableDamageMatchModule.java index e37aa81..f23a4ff 100644 --- a/PGM/src/main/java/tc/oc/pgm/damage/DisableDamageMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/damage/DisableDamageMatchModule.java @@ -3,6 +3,7 @@ package tc.oc.pgm.damage; import javax.annotation.Nullable; import javax.inject.Inject; +import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import org.bukkit.block.Block; @@ -36,12 +37,12 @@ public class DisableDamageMatchModule extends MatchModule implements Listener { this.causes = causes; } - @Override - public void load() { - super.load(); - if(MutationMatchModule.check(match, Mutation.NO_FALL)) { - this.causes.putAll(DamageCause.FALL, Sets.newHashSet(PlayerRelation.values())); - } + public SetMultimap causes() { + return causes; + } + + public ImmutableSetMultimap causesImmutable() { + return ImmutableSetMultimap.copyOf(causes()); } private static DamageCause getBlockDamageCause(Block block) { @@ -59,11 +60,11 @@ public class DisableDamageMatchModule extends MatchModule implements Listener { } private boolean canDamage(DamageCause cause, MatchPlayer victim, @Nullable ParticipantState damager) { - return !this.causes.containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), damager)); + return !causesImmutable().containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), damager)); } private boolean canDamage(DamageCause cause, MatchPlayer victim, DamageInfo info) { - return !this.causes.containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), info.getAttacker())); + return !causesImmutable().containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), info.getAttacker())); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) diff --git a/PGM/src/main/java/tc/oc/pgm/doublejump/DoubleJumpKit.java b/PGM/src/main/java/tc/oc/pgm/doublejump/DoubleJumpKit.java index 9e26b24..ca666a0 100644 --- a/PGM/src/main/java/tc/oc/pgm/doublejump/DoubleJumpKit.java +++ b/PGM/src/main/java/tc/oc/pgm/doublejump/DoubleJumpKit.java @@ -21,10 +21,23 @@ public class DoubleJumpKit extends Kit.Impl { this.rechargeInAir = rechargeInAir; } + public DoubleJumpKit() { + this(true, DEFAULT_POWER, DEFAULT_RECHARGE, true); + } + @Override public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { - DoubleJumpMatchModule djmm = player.getMatch().getMatchModule(DoubleJumpMatchModule.class); - if(djmm != null) djmm.setKit(player.getBukkit(), this); + player.getMatch().module(DoubleJumpMatchModule.class).ifPresent(jump -> jump.setKit(player.getBukkit(), this)); + } + + @Override + public boolean isRemovable() { + return true; + } + + @Override + public void remove(MatchPlayer player) { + player.getMatch().module(DoubleJumpMatchModule.class).ifPresent(jump -> jump.setKit(player.getBukkit(), null)); } public float chargePerTick() { diff --git a/PGM/src/main/java/tc/oc/pgm/effect/ProjectileTrailMatchModule.java b/PGM/src/main/java/tc/oc/pgm/effect/ProjectileTrailMatchModule.java index fed8163..3fc9cfe 100644 --- a/PGM/src/main/java/tc/oc/pgm/effect/ProjectileTrailMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/effect/ProjectileTrailMatchModule.java @@ -57,7 +57,7 @@ public class ProjectileTrailMatchModule extends MatchModule implements Listener return Math.max(0.001, rgb / 255.0); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onProjectileLaunch(ProjectileLaunchEvent event) { match.player(event.getActor()).ifPresent(shooter -> { final Projectile projectile = event.getEntity(); diff --git a/PGM/src/main/java/tc/oc/pgm/filters/matcher/match/MatchMutationFilter.java b/PGM/src/main/java/tc/oc/pgm/filters/matcher/match/MatchMutationFilter.java index 80ce970..c5f7024 100644 --- a/PGM/src/main/java/tc/oc/pgm/filters/matcher/match/MatchMutationFilter.java +++ b/PGM/src/main/java/tc/oc/pgm/filters/matcher/match/MatchMutationFilter.java @@ -15,7 +15,7 @@ public class MatchMutationFilter extends TypedFilter.Impl { @Override public boolean matches(IMatchQuery query) { return query.module(MutationMatchModule.class) - .filter(mmm -> mmm.getActiveMutations().contains(mutation)) + .filter(mmm -> mmm.enabled(mutation)) .isPresent(); } } diff --git a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRule.java b/PGM/src/main/java/tc/oc/pgm/gamerules/GameRule.java deleted file mode 100644 index 59bc83d..0000000 --- a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRule.java +++ /dev/null @@ -1,30 +0,0 @@ -package tc.oc.pgm.gamerules; - -public enum GameRule { - DO_FIRE_TICK("doFireTick"), - DO_MOB_LOOT("doMobLoot"), - DO_TILE_DROPS("doTileDrops"), - MOB_GRIEFING("mobGriefing"), - NATURAL_REGENERATION("naturalRegeneration"), - DO_DAYLIGHT_CYCLE("doDaylightCycle"); - - private String value; - - GameRule(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public static GameRule forName(String query) { - for (GameRule gamerule : values()) { - if (gamerule.getValue().equalsIgnoreCase(query)) { - return gamerule; - } - } - - return null; - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesMatchModule.java b/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesMatchModule.java index 85123f3..ebb20da 100644 --- a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesMatchModule.java @@ -6,29 +6,38 @@ import com.google.common.collect.ImmutableMap; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchModule; -import tc.oc.pgm.mutation.Mutation; -import tc.oc.pgm.mutation.MutationMatchModule; +import tc.oc.pgm.match.Repeatable; +import tc.oc.time.Time; public class GameRulesMatchModule extends MatchModule { - private final Map gameRules; + private final Map gameRules; - public GameRulesMatchModule(Match match, Map gameRules) { + public GameRulesMatchModule(Match match, Map gameRules) { super(match); this.gameRules = Preconditions.checkNotNull(gameRules, "gamerules"); - if(MutationMatchModule.check(match, Mutation.UHC)) { - this.gameRules.put(GameRule.NATURAL_REGENERATION, Boolean.FALSE); - } } @Override public void load() { - for (Map.Entry gameRule : this.gameRules.entrySet()) { - this.match.getWorld().setGameRuleValue(gameRule.getKey().getValue(), gameRule.getValue().toString()); - } + update(); } - public ImmutableMap getGameRules() { - return ImmutableMap.copyOf(gameRules); + @Repeatable(interval = @Time(seconds = 1)) + public void tick() { + update(); } + + public void update() { + gameRulesImmutable().forEach((String rule, String val) -> match.getWorld().setGameRuleValue(rule, val)); + } + + public Map gameRules() { + return gameRules; + } + + public ImmutableMap gameRulesImmutable() { + return ImmutableMap.copyOf(gameRules()); + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesModule.java b/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesModule.java index dfb1e37..b3191b9 100644 --- a/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesModule.java +++ b/PGM/src/main/java/tc/oc/pgm/gamerules/GameRulesModule.java @@ -18,9 +18,9 @@ import tc.oc.pgm.xml.InvalidXMLException; @ModuleDescription(name="Gamerules", follows = MutationMapModule.class) public class GameRulesModule implements MapModule, MatchModuleFactory { - private Map gameRules; + private Map gameRules; - private GameRulesModule(Map gamerules) { + private GameRulesModule(Map gamerules) { this.gameRules = gamerules; } @@ -33,32 +33,32 @@ public class GameRulesModule implements MapModule, MatchModuleFactory gameRules = new HashMap<>(); + Map gameRules = new HashMap<>(); for (Element gameRulesElement : doc.getRootElement().getChildren("gamerules")) { for (Element gameRuleElement : gameRulesElement.getChildren()) { - GameRule gameRule = GameRule.forName(gameRuleElement.getName()); + String gameRule = gameRuleElement.getName(); String value = gameRuleElement.getValue(); - if (gameRule == null) { + if(gameRule == null) { throw new InvalidXMLException(gameRuleElement.getName() + " is not a valid gamerule", gameRuleElement); } - if (value == null) { - throw new InvalidXMLException("Missing value for gamerule " + gameRule.getValue(), gameRuleElement); + if(value == null) { + throw new InvalidXMLException("Missing value for gamerule " + gameRule, gameRuleElement); } else if (!(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))) { throw new InvalidXMLException(gameRuleElement.getValue() + " is not a valid gamerule value", gameRuleElement); } - if (gameRules.containsKey(gameRule)){ - throw new InvalidXMLException(gameRule.getValue() + " has already been specified", gameRuleElement); + if(gameRules.containsKey(gameRule)){ + throw new InvalidXMLException(gameRule + " has already been specified", gameRuleElement); } - gameRules.put(gameRule, Boolean.valueOf(value)); + gameRules.put(gameRule, value); } } return new GameRulesModule(gameRules); } - public ImmutableMap getGameRules() { + public ImmutableMap getGameRules() { return ImmutableMap.copyOf(this.gameRules); } diff --git a/PGM/src/main/java/tc/oc/pgm/killreward/KillReward.java b/PGM/src/main/java/tc/oc/pgm/killreward/KillReward.java index 788621d..ad6072a 100644 --- a/PGM/src/main/java/tc/oc/pgm/killreward/KillReward.java +++ b/PGM/src/main/java/tc/oc/pgm/killreward/KillReward.java @@ -3,6 +3,8 @@ package tc.oc.pgm.killreward; import com.google.common.collect.ImmutableList; import org.bukkit.inventory.ItemStack; import tc.oc.pgm.filters.Filter; +import tc.oc.pgm.filters.matcher.StaticFilter; +import tc.oc.pgm.kits.ItemKit; import tc.oc.pgm.kits.Kit; public class KillReward { @@ -15,4 +17,8 @@ public class KillReward { this.filter = filter; this.kit = kit; } + + public KillReward(ItemKit kit) { + this(ImmutableList.of(kit.item()), StaticFilter.ALLOW, kit); + } } diff --git a/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardMatchModule.java b/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardMatchModule.java index 4832d13..ebca5ab 100644 --- a/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardMatchModule.java @@ -2,11 +2,10 @@ package tc.oc.pgm.killreward; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import javax.annotation.Nullable; -import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import org.bukkit.event.Event; @@ -27,34 +26,38 @@ import tc.oc.pgm.tracker.damage.DamageInfo; @ListenerScope(MatchScope.RUNNING) public class KillRewardMatchModule extends MatchModule implements Listener { - protected final ImmutableList killRewards; - protected final Multimap deadPlayerRewards = ArrayListMultimap.create(); + + private final List killRewards; + private final Multimap deadPlayerRewards = ArrayListMultimap.create(); public KillRewardMatchModule(Match match, List killRewards) { super(match); - this.killRewards = ImmutableList.copyOf(killRewards); + this.killRewards = killRewards; } - private Collection getRewards(@Nullable Event event, ParticipantState victim, DamageInfo damageInfo) { + public List rewards() { + return killRewards; + } + + public ImmutableList rewardsImmutable() { + return ImmutableList.copyOf(killRewards); + } + + public List rewards(@Nullable Event event, ParticipantState victim, DamageInfo damageInfo) { final DamageQuery query = DamageQuery.attackerDefault(event, victim, damageInfo); - return Collections2.filter(killRewards, new Predicate() { - @Override - public boolean apply(KillReward killReward) { - return killReward.filter.query(query).isAllowed(); - } - }); + return rewardsImmutable().stream().filter(reward -> reward.filter.query(query).isAllowed()).collect(Collectors.toList()); } - private Collection getRewards(MatchPlayerDeathEvent event) { - return getRewards(event, event.getVictim().getParticipantState(), event.getDamageInfo()); + public List rewards(MatchPlayerDeathEvent event) { + return rewards(event, event.getVictim().getParticipantState(), event.getDamageInfo()); } - private void giveRewards(MatchPlayer killer, Collection rewards) { - for(KillReward reward : rewards) { + public void giveRewards(MatchPlayer killer, Collection rewards) { + rewards.forEach(reward -> { // Apply kit first so it can not override reward items reward.kit.apply(killer); reward.items.forEach(stack -> ItemKitApplicator.fireEventAndTransfer(killer, stack)); - } + }); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -63,7 +66,7 @@ public class KillRewardMatchModule extends MatchModule implements Listener { MatchPlayer killer = event.getOnlineKiller(); if(killer == null) return; - Collection rewards = getRewards(event); + List rewards = rewards(event); if(killer.isDead()) { // If a player earns a KW while dead, give it to them when they respawn. Rationale: If they click respawn @@ -87,4 +90,5 @@ public class KillRewardMatchModule extends MatchModule implements Listener { public void onPartyChange(PlayerPartyChangeEvent event) { deadPlayerRewards.removeAll(event.getPlayer()); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardModule.java b/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardModule.java index 9f9c251..125a230 100644 --- a/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardModule.java +++ b/PGM/src/main/java/tc/oc/pgm/killreward/KillRewardModule.java @@ -1,5 +1,6 @@ package tc.oc.pgm.killreward; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.logging.Logger; @@ -27,10 +28,10 @@ import tc.oc.pgm.xml.InvalidXMLException; @ModuleDescription(name="Kill Reward") public class KillRewardModule implements MapModule, MatchModuleFactory { - protected final ImmutableList rewards; + protected final List rewards; public KillRewardModule(List rewards) { - this.rewards = ImmutableList.copyOf(rewards); + this.rewards = rewards; } @Override @@ -43,7 +44,7 @@ public class KillRewardModule implements MapModule, MatchModuleFactory rewards = ImmutableList.builder(); + List rewards = new ArrayList<>(); final ItemParser itemParser = context.needModule(ItemParser.class); final Optional itemModifier = context.module(ItemModifyModule.class); @@ -61,11 +62,6 @@ public class KillRewardModule implements MapModule, MatchModuleFactory list = rewards.build(); - if(list.isEmpty()) { - return null; - } else { - return new KillRewardModule(list); - } + return new KillRewardModule(rewards); } } diff --git a/PGM/src/main/java/tc/oc/pgm/kits/FreeItemKit.java b/PGM/src/main/java/tc/oc/pgm/kits/FreeItemKit.java index 85f5456..0f72f00 100644 --- a/PGM/src/main/java/tc/oc/pgm/kits/FreeItemKit.java +++ b/PGM/src/main/java/tc/oc/pgm/kits/FreeItemKit.java @@ -1,8 +1,15 @@ package tc.oc.pgm.kits; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import tc.oc.commons.bukkit.inventory.Slot; import tc.oc.pgm.match.MatchPlayer; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + public class FreeItemKit extends BaseItemKit { protected final ItemStack item; @@ -20,4 +27,36 @@ public class FreeItemKit extends BaseItemKit { public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { items.add(item); } + + @Override + public boolean isRemovable() { + return true; + } + + @Override + public void remove(MatchPlayer player) { + int left = item.getAmount(); + PlayerInventory inv = player.getInventory(); + for(Map.Entry> entry : Slot.Player.player() + .collect(Collectors.toMap(Function.identity(), slot -> slot.item(inv))).entrySet()) { + Slot.Player slot = entry.getKey(); + Optional itemMaybe = entry.getValue(); + if(itemMaybe.isPresent() && this.item.isSimilar(itemMaybe.get())) { + ItemStack item = itemMaybe.get(); + int delta = item.getAmount() - left; + if(delta > 0) { + ItemStack replaced = item.clone(); + replaced.setAmount(delta); + slot.putItem(inv, replaced); + break; + } else { + slot.putItem(inv, null); + if(delta < 0) { + left = -delta; + } + } + } + } + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/kits/HealthKit.java b/PGM/src/main/java/tc/oc/pgm/kits/HealthKit.java index bcbf76e..7eae5a4 100644 --- a/PGM/src/main/java/tc/oc/pgm/kits/HealthKit.java +++ b/PGM/src/main/java/tc/oc/pgm/kits/HealthKit.java @@ -1,13 +1,11 @@ package tc.oc.pgm.kits; -import com.google.common.base.Preconditions; import tc.oc.pgm.match.MatchPlayer; public class HealthKit extends Kit.Impl { protected final int halfHearts; public HealthKit(int halfHearts) { - Preconditions.checkArgument(0 < halfHearts && halfHearts <= 20, "halfHearts must be greater than 0 and less than or equal to 20"); this.halfHearts = halfHearts; } diff --git a/PGM/src/main/java/tc/oc/pgm/kits/NaturalRegenerationKit.java b/PGM/src/main/java/tc/oc/pgm/kits/NaturalRegenerationKit.java index 7b76875..1f41f51 100644 --- a/PGM/src/main/java/tc/oc/pgm/kits/NaturalRegenerationKit.java +++ b/PGM/src/main/java/tc/oc/pgm/kits/NaturalRegenerationKit.java @@ -11,12 +11,27 @@ public class NaturalRegenerationKit extends Kit.Impl { this.enabled = enabled; } - @Override - public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { + public void toggle(MatchPlayer player, boolean enabled) { if(fast) { player.getBukkit().setFastNaturalRegeneration(enabled); } else { player.getBukkit().setSlowNaturalRegeneration(enabled); } } + + @Override + public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { + toggle(player, enabled); + } + + @Override + public boolean isRemovable() { + return true; + } + + @Override + public void remove(MatchPlayer player) { + toggle(player, !enabled); + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/kits/SlotItemKit.java b/PGM/src/main/java/tc/oc/pgm/kits/SlotItemKit.java index 621be9d..22bd186 100644 --- a/PGM/src/main/java/tc/oc/pgm/kits/SlotItemKit.java +++ b/PGM/src/main/java/tc/oc/pgm/kits/SlotItemKit.java @@ -21,4 +21,5 @@ public class SlotItemKit extends FreeItemKit { public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { items.put(slot, item, force); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/listeners/MatchAnnouncer.java b/PGM/src/main/java/tc/oc/pgm/listeners/MatchAnnouncer.java index 9f16bb9..79a7f22 100644 --- a/PGM/src/main/java/tc/oc/pgm/listeners/MatchAnnouncer.java +++ b/PGM/src/main/java/tc/oc/pgm/listeners/MatchAnnouncer.java @@ -164,11 +164,11 @@ public class MatchAnnouncer implements PluginFacet, Listener { } final MutationMatchModule mmm = viewer.getMatch().getMatchModule(MutationMatchModule.class); - if(mmm != null && mmm.getActiveMutations().size() > 0) { + if(mmm != null && mmm.mutationsActive().size() > 0) { viewer.sendMessage( new Component(" ", ChatColor.DARK_GRAY).extra( new TranslatableComponent("broadcast.welcomeMessage.mutations", - new ListComponent(Collections2.transform(mmm.getActiveMutations(), Mutation.toComponent(ChatColor.GREEN))) + new ListComponent(Collections2.transform(mmm.mutationsActive(), Mutation.toComponent(ChatColor.GREEN))) ) ) ); diff --git a/PGM/src/main/java/tc/oc/pgm/listeners/PGMListener.java b/PGM/src/main/java/tc/oc/pgm/listeners/PGMListener.java index 8a8f29d..9f6a047 100644 --- a/PGM/src/main/java/tc/oc/pgm/listeners/PGMListener.java +++ b/PGM/src/main/java/tc/oc/pgm/listeners/PGMListener.java @@ -25,7 +25,6 @@ import tc.oc.pgm.Config; import tc.oc.pgm.events.MatchBeginEvent; import tc.oc.pgm.events.MatchEndEvent; import tc.oc.pgm.events.MatchLoadEvent; -import tc.oc.pgm.gamerules.GameRule; import tc.oc.pgm.gamerules.GameRulesModule; import tc.oc.pgm.match.MatchManager; import tc.oc.pgm.modules.TimeLockModule; @@ -99,9 +98,11 @@ public class PGMListener implements PluginFacet, Listener { // Time Lock // lock time before, during (if time lock enabled), and after the match // + static final String DO_DAYLIGHT_CYCLE = "doDaylightCycle"; + @EventHandler public void lockTime(final MatchLoadEvent event) { - event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(false)); + event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(false)); } @EventHandler @@ -113,16 +114,16 @@ public class PGMListener implements PluginFacet, Listener { GameRulesModule gameRulesModule = event.getMatch().getModuleContext().getModule(GameRulesModule.class); - if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(GameRule.DO_DAYLIGHT_CYCLE)) { - unlockTime = gameRulesModule.getGameRules().get(GameRule.DO_DAYLIGHT_CYCLE); + if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(DO_DAYLIGHT_CYCLE)) { + unlockTime = Boolean.valueOf(gameRulesModule.getGameRules().get(DO_DAYLIGHT_CYCLE)); } - event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(unlockTime)); + event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(unlockTime)); } @EventHandler public void lockTime(final MatchEndEvent event) { - event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(false)); + event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(false)); } @EventHandler diff --git a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java index a7a28d9..500ae8b 100644 --- a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java @@ -4,22 +4,21 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.pgm.events.ListenerScope; -import tc.oc.pgm.filters.matcher.StaticFilter; import tc.oc.pgm.match.Match; import tc.oc.pgm.filters.Filter; import tc.oc.pgm.filters.query.EntitySpawnQuery; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.mutation.Mutation; import tc.oc.pgm.mutation.MutationMatchModule; @ListenerScope(MatchScope.LOADED) public class MobsMatchModule extends MatchModule implements Listener { + private final Filter mobsFilter; public MobsMatchModule(Match match, Filter mobsFilter) { super(match); - this.mobsFilter = MutationMatchModule.check(match, Mutation.MOBS) ? StaticFilter.ALLOW : mobsFilter; + this.mobsFilter = mobsFilter; } @Override @@ -40,8 +39,10 @@ public class MobsMatchModule extends MatchModule implements Listener { @EventHandler(ignoreCancelled = true) public void checkSpawn(final CreatureSpawnEvent event) { - if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { + if(!match.module(MutationMatchModule.class).map(mmm -> mmm.allowMob(event.getSpawnReason())).orElse(false) && + event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { event.setCancelled(mobsFilter.query(new EntitySpawnQuery(event, event.getEntity(), event.getSpawnReason())).isDenied()); } } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java index be781b7..575af58 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java @@ -7,58 +7,61 @@ import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TranslatableComponent; -import tc.oc.api.docs.virtual.MatchDoc; import tc.oc.commons.core.chat.Component; -import tc.oc.pgm.mutation.submodule.MutationModule; +import tc.oc.pgm.PGM; +import tc.oc.pgm.mutation.types.MutationModule; +import tc.oc.pgm.mutation.types.kit.ArmorMutation; +import tc.oc.pgm.mutation.types.kit.ElytraMutation; +import tc.oc.pgm.mutation.types.kit.EnchantmentMutation; +import tc.oc.pgm.mutation.types.kit.EquestrianMutation; +import tc.oc.pgm.mutation.types.kit.ExplosiveMutation; +import tc.oc.pgm.mutation.types.kit.GlowMutation; +import tc.oc.pgm.mutation.types.kit.HardcoreMutation; +import tc.oc.pgm.mutation.types.kit.HealthMutation; +import tc.oc.pgm.mutation.types.kit.JumpMutation; +import tc.oc.pgm.mutation.types.kit.MobsMutation; +import tc.oc.pgm.mutation.types.kit.PotionMutation; +import tc.oc.pgm.mutation.types.kit.ProjectileMutation; +import tc.oc.pgm.mutation.types.kit.StealthMutation; +import tc.oc.pgm.mutation.types.other.RageMutation; +import tc.oc.pgm.mutation.types.targetable.ApocalypseMutation; +import tc.oc.pgm.mutation.types.targetable.BomberMutation; +import tc.oc.pgm.mutation.types.targetable.LightningMutation; -import static tc.oc.pgm.mutation.submodule.MutationModules.*; +import java.util.stream.Stream; public enum Mutation { - BLITZ (null, false), - UHC (null, false), - EXPLOSIVES (Explosives.class, true), - NO_FALL (null, false), - MOBS (null, false), - STRENGTH (Strength.class, true), - DOUBLE_JUMP (DoubleJump.class, true), - INVISIBILITY(Invisibility.class, true), - LIGHTNING (Lightning.class, true), - RAGE (Rage.class, true), - ELYTRA (Elytra.class, true); + BLITZ (null), + RAGE (RageMutation.class), + HARDCORE (HardcoreMutation.class), + JUMP (JumpMutation.class), + EXPLOSIVE (ExplosiveMutation.class), + ELYTRA (ElytraMutation.class), + PROJECTILE (ProjectileMutation.class), + ENCHANTMENT(EnchantmentMutation.class), + POTION (PotionMutation.class), + EQUESTRIAN (EquestrianMutation.class), + HEALTH (HealthMutation.class), + GLOW (GlowMutation.class), + STEALTH (StealthMutation.class), + ARMOR (ArmorMutation.class), + MOBS (MobsMutation.class), + LIGHTNING (LightningMutation.class), + BOMBER (BomberMutation.class), + APOCALYPSE (ApocalypseMutation.class); public static final String TYPE_KEY = "mutation.type."; public static final String DESCRIPTION_KEY = ".desc"; - /** - * The module class that handles this mutation. - */ - private final @Nullable Class clazz; + private final @Nullable Class loader; - /** - * Whether this mutation be changed during a match. - */ - private final boolean change; - - Mutation(@Nullable Class clazz, boolean change) { - this.clazz = clazz; - this.change = change; + Mutation(@Nullable Class loader) { + this.loader = loader; } - public static Mutation fromApi(MatchDoc.Mutation mutation) { - return values()[mutation.ordinal()]; - } - - public MatchDoc.Mutation toApi() { - return MatchDoc.Mutation.values()[ordinal()]; - } - - public Class getModuleClass() { - return clazz; - } - - public boolean isChangeable() { - return change; + public Class loader() { + return loader; } public String getName() { @@ -76,4 +79,14 @@ public enum Mutation { public static Function toComponent(final ChatColor color) { return mutation -> mutation.getComponent(color); } + + public static Stream fromString(final String name) { + try { + return Stream.of(Mutation.valueOf(name)); + } catch(IllegalArgumentException iae) { + PGM.get().getLogger().warning("Unable to find mutation named '" + name + "'"); + return Stream.empty(); + } + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java index d99d82c..db3046f 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java @@ -1,24 +1,24 @@ package tc.oc.pgm.mutation; -import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.commons.core.util.MapUtils; import tc.oc.commons.core.random.RandomUtils; import tc.oc.pgm.Config; import tc.oc.pgm.match.*; import tc.oc.pgm.mutation.command.MutationCommands; -import tc.oc.pgm.mutation.submodule.MutationModule; +import tc.oc.pgm.mutation.types.MutationModule; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; import java.util.*; import java.util.logging.Level; +import java.util.stream.Stream; import javax.inject.Inject; public class MutationMatchModule extends MatchModule { - // TODO: send remote mutation alerts via an AMQP message /** * Chance that a mutation event will occur. @@ -63,39 +63,36 @@ public class MutationMatchModule extends MatchModule { this.chance = options.chance; this.amount = options.amount; this.weightedSelector = new ImmutableWeightedRandomChooser<>(options.weights); - this.mutations = getDefaultMutations(); + this.mutations = mutationsDefault(); this.history = new HashSet<>(); this.modules = new HashMap<>(); } - public final ImmutableMap getMutations() { + public final ImmutableMap mutations() { return ImmutableMap.copyOf(mutations); } - public final ImmutableSet getActiveMutations() { - return ImmutableSet.copyOf(Collections2.filter(getMutations().keySet(), new Predicate() { - @Override - public boolean apply(Mutation mutation) { - return mutations.get(mutation); - } - })); + public final ImmutableSet mutationsActive() { + return ImmutableSet.copyOf(Collections2.filter(mutations().keySet(), mutations::get)); } - public final ImmutableSet getHistoricalMutations() { + public final ImmutableSet mutationsHistorical() { return ImmutableSet.copyOf(history); } - public final ImmutableSet getMutationModules() { + private Map mutationsDefault() { + Map defaults = new HashMap<>(); + MapUtils.putAll(defaults, Sets.newHashSet(Mutation.values()), false); + return defaults; + } + + public final ImmutableSet mutationModules() { return ImmutableSet.copyOf(modules.values()); } - @Override - public boolean shouldLoad() { - return Config.Mutations.enabled(); - } - @Override public void load() { + if(!Config.Mutations.enabled()) return; Random random = match.getRandom(); // Check if the api has any queued mutations Collection queuedMutations = mutationQueue.mutations(); @@ -113,7 +110,7 @@ public class MutationMatchModule extends MatchModule { mutationQueue.clear(); } // Load the mutation modules for this match - for(Mutation mutation : getActiveMutations()) { + for(Mutation mutation : mutationsActive()) { try { mutate(mutation); } catch (Throwable throwable) { @@ -124,43 +121,53 @@ public class MutationMatchModule extends MatchModule { @Override public void enable() { - for(MutationModule module : modules.values()) { - module.enable(match.isRunning()); - } + modules.values().forEach(MutationModule::enable); } @Override public void disable() { - for(MutationModule module : modules.values()) { - module.disable(match.isRunning()); - } + modules.values().forEach(MutationModule::disable); + } + + public void register(Mutation mutation, boolean load) { + mutations.put(mutation, load); } public void mutate(Mutation mutation) throws Throwable { - Class clazz = mutation.getModuleClass(); - if(clazz == null || (match.isRunning() && !mutation.isChangeable())) return; - MutationModule module = modules.containsKey(clazz) ? modules.get(clazz) : mutation.getModuleClass().getDeclaredConstructor(Match.class).newInstance(match); + Class loader = mutation.loader(); + if(loader == null) return; + MutationModule module = modules.containsKey(loader) ? modules.get(loader) : loader.getDeclaredConstructor(Match.class).newInstance(match); if(mutations.get(mutation)) { - module.enable(match.isRunning()); - modules.put(clazz, module); + module.enable(); + modules.put(loader, module); mutations.put(mutation, true); history.add(mutation); } else { - module.disable(match.isRunning()); - modules.remove(clazz); + module.disable(); + modules.remove(loader); mutations.put(mutation, false); } } - private Map getDefaultMutations() { - Map defaults = new HashMap<>(); - MapUtils.putAll(defaults, Sets.newHashSet(Mutation.values()), false); - return defaults; + public boolean enabled() { + return !mutationsActive().isEmpty(); } - public static boolean check(Match match, Mutation mutation) { - return Config.Mutations.enabled() && - match.hasMatchModule(MutationMatchModule.class) && - match.getMatchModule(MutationMatchModule.class).getActiveMutations().contains(mutation); + public boolean enabled(Mutation... mutations) { + return mutationsActive().stream().anyMatch(m1 -> Stream.of(mutations).anyMatch(m2 -> m2.equals(m1))); } + + public boolean allowMob(CreatureSpawnEvent.SpawnReason reason) { + switch(reason) { + case NATURAL: + case DEFAULT: + case CHUNK_GEN: + case JOCKEY: + case MOUNT: + return false; + default: + return true; + } + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/MutationQueue.java b/PGM/src/main/java/tc/oc/pgm/mutation/MutationQueue.java index 2990517..1cc2fe4 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/MutationQueue.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/MutationQueue.java @@ -6,7 +6,6 @@ import java.util.HashSet; import java.util.stream.Collectors; import javax.inject.Inject; -import com.google.common.collect.Collections2; import com.google.common.util.concurrent.ListenableFuture; import tc.oc.api.docs.Server; import tc.oc.api.docs.virtual.ServerDoc; @@ -28,12 +27,12 @@ public class MutationQueue { .getLocalServer() .queued_mutations() .stream() - .map(mutation -> Mutation.values()[mutation.ordinal()]) + .flatMap(Mutation::fromString) .collect(Collectors.toList()); } public ListenableFuture clear() { - return force(Collections.emptyList()); + return force(Collections.emptyList()); } public ListenableFuture removeAll(final Collection mutations) { @@ -53,6 +52,7 @@ public class MutationQueue { } private ListenableFuture force(final Collection mutations) { - return minecraftService.updateLocalServer((ServerDoc.Mutation) () -> mutations.stream().map(Mutation::toApi).collect(Collectors.toSet())); + return minecraftService.updateLocalServer((ServerDoc.Mutation) () -> mutations.stream().map(Mutation::name).collect(Collectors.toSet())); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java b/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java index cecae18..80856f0 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java @@ -2,20 +2,21 @@ package tc.oc.pgm.mutation.command; import java.util.Collection; import java.util.HashSet; +import java.util.logging.Level; import javax.inject.Inject; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.NestedCommand; +import com.sk89q.minecraft.util.commands.*; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.command.CommandSender; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.bukkit.chat.WarningComponent; +import tc.oc.commons.bukkit.nick.IdentityProvider; +import tc.oc.commons.core.chat.Audience; import tc.oc.minecraft.scheduler.SyncExecutor; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.ListComponent; @@ -56,39 +57,42 @@ public class MutationCommands implements NestedCommands { private final SyncExecutor syncExecutor; private final Audiences audiences; private final MutationQueue mutationQueue; + private final IdentityProvider identityProvider; - @Inject - MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue) { + @Inject MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue, IdentityProvider identityProvider) { this.syncExecutor = syncExecutor; this.audiences = audiences; this.mutationQueue = mutationQueue; + this.identityProvider = identityProvider; } @Command( - aliases = {"enable", "add"}, + aliases = {"enable", "e"}, desc = "Adds a mutation to the upcoming match." + "You can use '?' as a wildcard or " + "'*' to use all.", usage = "", + flags = "q", min = 1, max = 1 ) @CommandPermissions(PERMISSION_SET) - public void enable(CommandContext args, CommandSender sender) throws CommandException { + public void enable(CommandContext args, CommandSender sender) throws CommandException, SuggestException { set(args, sender, true); } @Command( - aliases = {"disable", "remove"}, + aliases = {"disable", "d"}, desc = "Remove a mutation to the upcoming match." + "You can use '?' as a wildcard or " + "'*' to use all.", usage = "", + flags = "q", min = 1, max = 1 ) @CommandPermissions(PERMISSION_SET) - public void disable(CommandContext args, CommandSender sender) throws CommandException { + public void disable(CommandContext args, CommandSender sender) throws CommandException, SuggestException { set(args, sender, false); } @@ -105,8 +109,8 @@ public class MutationCommands implements NestedCommands { public void list(final CommandContext args, CommandSender sender) throws CommandException { MutationMatchModule module = verify(sender); final boolean queued = args.hasFlag('q'); - final Collection active = queued ? mutationQueue.mutations() : module.getActiveMutations(); - new Paginator() { + final Collection active = queued ? mutationQueue.mutations() : module.mutationsActive(); + new Paginator(Mutation.values().length / 2) { @Override protected BaseComponent title() { return new TranslatableComponent(queued ? "command.mutation.list.queued" : "command.mutation.list.current"); @@ -124,20 +128,26 @@ public class MutationCommands implements NestedCommands { return CommandUtils.getMatchModule(MutationMatchModule.class, sender); } - public void set(CommandContext args, final CommandSender sender, final boolean value) throws CommandException { + public void set(CommandContext args, final CommandSender sender, final boolean value) throws CommandException, SuggestException { final MutationMatchModule module = verify(sender); final Match match = module.getMatch(); String action = args.getString(0); + boolean queued = args.hasFlag('q') || match.isFinished(); // Mutations that *will* be added or removed final Collection mutations = new HashSet<>(); // Mutations that *are allowed* to be added or removed final Collection availableMutations = Sets.newHashSet(Mutation.values()); - final Collection queue = mutationQueue.mutations(); + final Collection queue = queued ? mutationQueue.mutations() : module.mutationsActive(); if(value) availableMutations.removeAll(queue); else availableMutations.retainAll(queue); // Check if all mutations have been enabled/disabled if((queue.size() == Mutation.values().length && value) || (queue.isEmpty() && !value)) { - throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled" : "command.mutation.error.disabled")); + throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled.all" : "command.mutation.error.disabled.all")); + } + // Suggest mutations for the user to choose + final SuggestionContext context = args.getSuggestionContext(); + if(context != null) { + context.suggestArgument(0, StringUtils.complete(context.getPrefix(), availableMutations.stream().map(mutation -> mutation.name().toLowerCase()))); } // Get which action the user wants to preform switch (action) { @@ -145,27 +155,50 @@ public class MutationCommands implements NestedCommands { case "?": mutations.add(Iterables.get(availableMutations, RandomUtils.safeNextInt(match.getRandom(), availableMutations.size()))); break; default: Mutation query = StringUtils.bestFuzzyMatch(action, Sets.newHashSet(Mutation.values()), 0.9); - if (query == null) { + if(query == null) { throw newCommandException(sender, new TranslatableComponent("command.mutation.error.find", action)); + } else if(value == queue.contains(query)) { + throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled" : "command.mutation.error.disabled", query.getComponent(ChatColor.RED))); } else { mutations.add(query); } } - - // Send the queued changes off to the api - syncExecutor.callback( - value ? mutationQueue.mergeAll(mutations) - : mutationQueue.removeAll(mutations), - result -> { - audiences.get(sender).sendMessage(new Component(new TranslatableComponent( - message(false, value, mutations.size() == 1), - new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA))) - ), ChatColor.WHITE)); + Audience origin = audiences.get(sender); + Audience all = audiences.localServer(); + String message = message(!queued, value, mutations.size() == 1); + ListComponent changed = new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA))); + if(queued) { + // Send the queued changes off to the api + syncExecutor.callback( + value ? mutationQueue.mergeAll(mutations) + : mutationQueue.removeAll(mutations), + result -> { + origin.sendMessage(new Component(new TranslatableComponent(message, changed), ChatColor.WHITE)); + } + ); + } else { + // Make the changes immediately + for(Mutation mutation : mutations) { + try { + module.register(mutation, value); + module.mutate(mutation); + } catch(Throwable t) { + module.register(mutation, !value); + origin.sendMessage( + new WarningComponent( + "command.mutation.error.mutate", + mutation.getComponent(ChatColor.RED) + ) + ); + module.getLogger().log(Level.SEVERE, "Unable to enable/disable mutation", t); + return; + } } - ); + PlayerComponent player = new PlayerComponent(identityProvider.currentIdentity(sender)); + all.sendMessage(new Component(new TranslatableComponent(message, player, changed))); + } } - // TODO: force enabling mutations public String message(boolean now, boolean enable, boolean singular) { if(now) { if(enable) { diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/KitMutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/submodule/KitMutationModule.java deleted file mode 100644 index 965e2f7..0000000 --- a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/KitMutationModule.java +++ /dev/null @@ -1,28 +0,0 @@ -package tc.oc.pgm.mutation.submodule; - -import tc.oc.pgm.kits.Kit; -import tc.oc.pgm.match.Match; - -/** - * A mutation modules that injects special kits on participant spawn. - */ -public class KitMutationModule extends MutationModule { - - protected final Kit[] kits; - protected final boolean force; - - public KitMutationModule(Match match, boolean force, Kit... kits) { - super(match); - this.kits = kits; - this.force = force; - } - - public Kit[] getKits() { - return enabled ? kits : new Kit[0]; - } - - public boolean isForceful() { - return force; - } - -} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModule.java deleted file mode 100644 index 0b9b5f0..0000000 --- a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModule.java +++ /dev/null @@ -1,68 +0,0 @@ -package tc.oc.pgm.mutation.submodule; - -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import tc.oc.pgm.events.ListenerScope; -import tc.oc.pgm.match.Match; -import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.mutation.Mutation; -import tc.oc.pgm.mutation.MutationMatchModule; - -/** - * Bits of immutable code that manage a {@link Mutation}. - * - * This should be able to load at any time during the match - * and not cause any problems. This will allow mutations - * to be forcefully loaded on the fly without any worries - * of breaking the match state. - * - * TODO: Force loading is not been enabled yet, but all mutation - * modules should be ready for the switch. - */ -@ListenerScope(MatchScope.RUNNING) -public abstract class MutationModule implements Listener { - - protected final Match match; - protected boolean enabled; - - /** - * Constructed when {@link MutationMatchModule#load()} - * has been called. This will only be constructed if its - * subsequent {@link Mutation} is enabled for the match. - * - * @param match the match for this module. - */ - public MutationModule(Match match) { - this.match = match; - this.enabled = false; - } - - /** - * Called when the match starts. - * - * However, this should be able to be called at any - * point before the match ends and still work as expected. - * @param late called after the match starts. - * - * {@link MutationMatchModule#enable()} - */ - public void enable(boolean late) { - match.registerEvents(this); - enabled = true; - } - - /** - * Called when the match ends. - * - * However, this should be able to be called at any - * point during a match and still work as expected. - * @param premature called before the match ends. - * - * {@link MutationMatchModule#disable()} - */ - public void disable(boolean premature) { - match.unregisterEvents(this); - enabled = false; - } - -} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModules.java b/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModules.java deleted file mode 100644 index 68bb6ab..0000000 --- a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/MutationModules.java +++ /dev/null @@ -1,110 +0,0 @@ -package tc.oc.pgm.mutation.submodule; - -import org.bukkit.Material; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.ExplosionPrimeEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.util.Vector; -import java.time.Duration; -import tc.oc.commons.bukkit.inventory.ArmorType; -import tc.oc.commons.bukkit.inventory.Slot; -import tc.oc.commons.bukkit.item.ItemUtils; -import tc.oc.pgm.doublejump.DoubleJumpKit; -import tc.oc.pgm.kits.FreeItemKit; -import tc.oc.pgm.kits.Kit; -import tc.oc.pgm.kits.KitNode; -import tc.oc.pgm.kits.PotionKit; -import tc.oc.pgm.kits.SlotItemKit; -import tc.oc.pgm.match.Match; -import tc.oc.pgm.match.ParticipantState; - -public class MutationModules { - - public static class Explosives extends KitMutationModule { - public static final Float MULTIPLIER = 1.75f; - private static final Kit KIT = KitNode.of(new FreeItemKit(new ItemStack(Material.FLINT_AND_STEEL)), - new FreeItemKit(new ItemStack(Material.TNT, 16))); - - public Explosives(Match match) { - super(match, false, KIT); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onExplosionPrime(ExplosionPrimeEvent event) { - event.setRadius(event.getRadius() * MULTIPLIER); - } - } - - public static class Strength extends KitMutationModule { - public static final PotionEffect EFFECT = new PotionEffect(PotionEffectType.INCREASE_DAMAGE, Integer.MAX_VALUE, 0, false, false); - public static final PotionKit KIT = new PotionKit(EFFECT); - - public Strength(Match match) { - super(match, true, KIT); - } - } - - public static class DoubleJump extends KitMutationModule { - public static final DoubleJumpKit KIT = new DoubleJumpKit(true, 3f, Duration.ofSeconds(5), true); - - public DoubleJump(Match match) { - super(match, false, KIT); - } - } - - public static class Invisibility extends KitMutationModule { - public static final PotionEffect EFFECT = new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false); - public static final PotionKit KIT = new PotionKit(EFFECT); - - public Invisibility(Match match) { - super(match, true, KIT); - } - } - - public static class Lightning extends TargetableMutationModule { - public static final Duration FREQUENCY = Duration.ofSeconds(30); - - public Lightning(Match match) { - super(match, FREQUENCY, 3); - } - - @Override - public void execute(ParticipantState player) { - match.getWorld().strikeLightning(player.getLocation().clone().add(Vector.getRandom())); - } - } - - public static class Rage extends MutationModule { - public static final Integer DAMAGE = Integer.MAX_VALUE; - - public Rage(Match match) { - super(match); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onPlayerDamage(EntityDamageByEntityEvent event) { - Entity damager = event.getDamager(); - if ((damager instanceof Player && ItemUtils.isWeapon(((Player) damager).getItemInHand())) || - (damager instanceof Projectile && ((Projectile) damager).getShooter() instanceof Player)) { - event.setDamage(DAMAGE); - } - } - } - - public static class Elytra extends KitMutationModule { - public static final Kit KIT = KitNode.of(new SlotItemKit(new ItemStack(Material.ELYTRA), Slot.Armor.forType(ArmorType.CHESTPLATE)), - new DoubleJumpKit(true, 6f, Duration.ofSeconds(30), true)); - - public Elytra(Match match) { - super(match, true, KIT); - } - } - -} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/TargetableMutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/submodule/TargetableMutationModule.java deleted file mode 100644 index 2097ed6..0000000 --- a/PGM/src/main/java/tc/oc/pgm/mutation/submodule/TargetableMutationModule.java +++ /dev/null @@ -1,46 +0,0 @@ -package tc.oc.pgm.mutation.submodule; - -import com.google.common.collect.Range; -import java.time.Duration; -import tc.oc.commons.core.random.Entropy; -import tc.oc.commons.core.scheduler.Task; -import tc.oc.commons.core.stream.Collectors; -import tc.oc.pgm.match.Match; -import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.match.ParticipantState; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A mutation module that executes a task on a random {@link ParticipantState}s. - */ -public abstract class TargetableMutationModule extends MutationModule { - - protected final Duration frequency; - protected final int targets; - protected Task task; - - public TargetableMutationModule(final Match match, Duration frequency, int targets) { - super(match); - this.frequency = checkNotNull(frequency, "frequency cannot be null"); - this.targets = targets; checkArgument(targets >= 1, "amount of targets cannot be less than 1"); - this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> { - final Entropy entropy = match.entropyForTick(); - match.participants() - .filter(MatchPlayer::isSpawned) - .collect(Collectors.toRandomSubList(entropy, entropy.randomInt(Range.closed(1, targets)))) - .forEach(player -> execute(player.getParticipantState())); - }); - } - - @Override - public void disable(boolean premature) { - task.cancel(); - super.disable(premature); - } - - public abstract void execute(ParticipantState player); - -} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java new file mode 100644 index 0000000..813022b --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java @@ -0,0 +1,111 @@ +package tc.oc.pgm.mutation.types; + +import org.bukkit.inventory.ItemStack; +import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.pgm.killreward.KillReward; +import tc.oc.pgm.killreward.KillRewardMatchModule; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.kits.KitPlayerFacet; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.stream.Stream; + +/** + * A mutation module that injects special kits on spawn and/or kill. + */ +public class KitMutation extends MutationModule { + + protected final List kits; + protected final Map> playerKits; + protected final Map> savedSlots; + protected final List rewards; + protected final boolean force; + + public KitMutation(Match match, boolean force) { + super(match); + this.kits = new ArrayList<>(); + this.playerKits = new WeakHashMap<>(); + this.savedSlots = new WeakHashMap<>(); + this.rewards = new ArrayList<>(); + this.force = force; + } + + public KitMutation(Match match, boolean force, Kit... kits) { + this(match, force); + Stream.of(kits).forEachOrdered(this.kits::add); + } + + /** + * Generates a list of kits to apply for the player. + * Called inside {@link #apply(MatchPlayer)}. + * @param player the player that will receive the kits + * @param kits a mutable list of kits. + */ + public void kits(MatchPlayer player, List kits) { + kits.addAll(this.kits); + } + + /** + * Apply the kits to the player. + * @param player the player. + */ + public void apply(MatchPlayer player) { + List kits = new ArrayList<>(); + kits(player, kits); + playerKits.put(player, kits); + saved().forEach(slot -> { + slot.item(player.getInventory()).ifPresent(item -> { + Map slots = savedSlots.getOrDefault(player, new HashMap<>()); + slots.put(slot, (ItemStack) item); + savedSlots.put(player, slots); + slot.putItem(player.getInventory(), null); + }); + }); + kits.forEach(kit -> player.facet(KitPlayerFacet.class).applyKit(kit, force)); + } + + /** + * Forcefuly remove kits from the player. + * @param player the player. + */ + public void remove(MatchPlayer player) { + playerKits.getOrDefault(player, new ArrayList<>()).stream().filter(Kit::isRemovable).forEach(kit -> kit.remove(player)); + savedSlots.getOrDefault(player, new HashMap<>()).forEach((Slot slot, ItemStack item) -> slot.putItem(player.getInventory(), item)); + } + + /** + * Any slots in the player's inventory that should be saved + * before {@link #apply(MatchPlayer)} and restored after {@link #remove(MatchPlayer)}. + * @return the saved slots. + */ + public Stream saved() { + return Stream.empty(); + } + + @Override + public void enable() { + super.enable(); + match.module(KillRewardMatchModule.class).get().rewards().addAll(rewards); + if(match.hasStarted()) { + match.participants().forEach(this::apply); + } + } + + @Override + public void disable() { + match.module(KillRewardMatchModule.class).get().rewards().removeAll(rewards); + match.participants().forEach(this::remove); + kits.clear(); + playerKits.clear(); + savedSlots.clear(); + rewards.clear(); + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java new file mode 100644 index 0000000..712073f --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java @@ -0,0 +1,96 @@ +package tc.oc.pgm.mutation.types; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Listener; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import tc.oc.commons.bukkit.item.ItemBuilder; +import tc.oc.commons.core.random.AdvancingEntropy; +import tc.oc.commons.core.random.Entropy; +import tc.oc.pgm.events.ListenerScope; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.mutation.Mutation; +import tc.oc.pgm.mutation.MutationMatchModule; + +import java.util.Random; + +/** + * Bits of immutable code that manage a {@link Mutation}. + * + * This should be able to load at any time during the match + * and not cause any problems. This will allow mutations + * to be forcefully loaded on the fly without any worries + * of breaking the match state. + */ +@ListenerScope(MatchScope.RUNNING) +public abstract class MutationModule implements Listener { + + protected final Match match; + protected final World world; + protected final Entropy entropy; + protected final Random random; + + /** + * Constructed when {@link MutationMatchModule#load()} + * has been called. This will only be constructed if its + * subsequent {@link Mutation} is enabled for the match. + * + * @param match the match for this module. + */ + public MutationModule(Match match) { + this.match = match; + this.world = match.getWorld(); + this.entropy = new AdvancingEntropy(); + this.random = new Random(); + } + + /** + * Called when the match starts. + * + * However, this should be able to be called at any + * point before the match ends and still work as expected. + */ + public void enable() { + match.registerEventsAndRepeatables(this); + } + + /** + * Called when the match ends. + * + * However, this should be able to be called at any + * point during a match and still work as expected. + */ + public void disable() { + match.unregisterEvents(this); + match.unregisterRepeatable(this); + } + + protected static ItemStack item(Material material, int amount) { + return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get(); + } + + protected static ItemStack item(Material material) { + return item(material, 1); + } + + protected E spawn(Location location, Class entityClass) { + E entity = world.spawn(location, entityClass); + if(entity instanceof LivingEntity) { + LivingEntity living = (LivingEntity) entity; + living.setCanPickupItems(false); + living.setRemoveWhenFarAway(true); + EntityEquipment equipment = living.getEquipment(); + equipment.setHelmetDropChance(0); + equipment.setChestplateDropChance(0); + equipment.setLeggingsDropChance(0); + equipment.setBootsDropChance(0); + } + return entity; + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java new file mode 100644 index 0000000..96f61f7 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java @@ -0,0 +1,64 @@ +package tc.oc.pgm.mutation.types; + +import java.time.Duration; +import java.util.List; + +import tc.oc.commons.core.scheduler.Task; +import tc.oc.commons.core.stream.Collectors; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.MatchScope; + +/** + * A mutation module that executes a task on random {@link MatchPlayer}s. + */ +public abstract class TargetMutation extends MutationModule { + + private final Duration frequency; + private Task task; + + public TargetMutation(final Match match, Duration frequency) { + super(match); + this.frequency = frequency; + } + + /** + * Execute a task on the given randomly selected players. + * @param players a list of players, which size is determined by {@link #targets()}. + */ + public abstract void execute(List players); + + /** + * Determine the number of random players to target. + * If there are no enough players on the server, it is possible + * that the number of targets is less than expected. + * @return number of targets. + */ + public abstract int targets(); + + /** + * Generate a list of random players. + * @return the random players. + */ + public List search() { + return match.participants() + .filter(MatchPlayer::isSpawned) + .collect(Collectors.toRandomSubList(entropy, targets())); + } + + @Override + public void enable() { + super.enable(); + this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> execute(search())); + } + + @Override + public void disable() { + if(task != null) { + task.cancel(); + task = null; + } + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ArmorMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ArmorMutation.java new file mode 100644 index 0000000..2f777bf --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ArmorMutation.java @@ -0,0 +1,78 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import tc.oc.commons.bukkit.inventory.ArmorType; +import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.bukkit.item.ItemUtils; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.ItemKitApplicator; +import tc.oc.pgm.kits.SlotItemKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; +import java.util.WeakHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ArmorMutation extends KitMutation { + + final static FreeItemKit SWORD = new FreeItemKit(item(Material.DIAMOND_SWORD)); + + final static SlotItemKit[] ARMOR = new SlotItemKit[] { + new SlotItemKit(item(Material.DIAMOND_HELMET), Slot.Armor.forType(ArmorType.HELMET)), + new SlotItemKit(item(Material.DIAMOND_CHESTPLATE), Slot.Armor.forType(ArmorType.CHESTPLATE)), + new SlotItemKit(item(Material.DIAMOND_LEGGINGS), Slot.Armor.forType(ArmorType.LEGGINGS)), + new SlotItemKit(item(Material.DIAMOND_BOOTS), Slot.Armor.forType(ArmorType.BOOTS)), + }; + + final WeakHashMap weapons; + + public ArmorMutation(Match match) { + super(match, true, ARMOR); + this.kits.add(SWORD); + weapons = new WeakHashMap<>(); + } + + @Override + public void apply(MatchPlayer player) { + // Find the player's first weapon and store it for later + List hotbar = Slot.Hotbar.hotbar() + .map(slot -> slot.getItem(player.getInventory())) + .collect(Collectors.toList()); + for(ItemStack item : hotbar) { + if(item != null && ItemUtils.isWeapon(item)) { + weapons.put(player, item); + player.getInventory().remove(item); + break; + } + } + super.apply(player); + } + + @Override + public void remove(MatchPlayer player) { + super.remove(player); + // Restore the player's old weapon + ItemStack weapon = weapons.remove(player); + if(weapon != null) { + ItemKitApplicator applicator = new ItemKitApplicator(); + applicator.add(weapon); + applicator.apply(player); + } + } + + @Override + public Stream saved() { + return Slot.Armor.armor(); + } + + @Override + public void disable() { + super.disable(); + weapons.clear(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java new file mode 100644 index 0000000..1040b95 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java @@ -0,0 +1,42 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.Material; +import tc.oc.commons.bukkit.inventory.ArmorType; +import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.pgm.doublejump.DoubleJumpKit; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.kits.ItemKitApplicator; +import tc.oc.pgm.kits.SlotItemKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.time.Duration; +import java.util.stream.Stream; + +public class ElytraMutation extends KitMutation { + + final static ItemKit ELYTRA = new SlotItemKit(item(Material.ELYTRA), Slot.Armor.forType(ArmorType.CHESTPLATE)); + final static DoubleJumpKit JUMP = new DoubleJumpKit(true, 6f, Duration.ofSeconds(30), true); + + public ElytraMutation(Match match) { + super(match, true, ELYTRA, JUMP); + } + + @Override + public Stream saved() { + return Stream.of(Slot.Armor.forType(ArmorType.CHESTPLATE)); + } + + @Override + public void remove(MatchPlayer player) { + // If the player is mid-air, give them a totem so they don't fall and die + if(player.getBukkit().isGliding()) { + ItemKitApplicator applicator = new ItemKitApplicator(); + applicator.put(Slot.OffHand.offHand(), item(Material.TOTEM), false); + applicator.apply(player); + } + super.remove(player); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java new file mode 100644 index 0000000..7675061 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java @@ -0,0 +1,159 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import tc.oc.commons.bukkit.item.ItemUtils; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +public class EnchantmentMutation extends KitMutation { + + final static ImmutableMap LEVELS_MAP = new ImmutableMap.Builder() + .put(1, 10) + .put(2, 3) + .put(3, 1) + .build(); + + final static ImmutableMap ARMOR_MAP = new ImmutableMap.Builder() + .put(Enchantment.PROTECTION_ENVIRONMENTAL, 15) + .put(Enchantment.PROTECTION_PROJECTILE, 10) + .put(Enchantment.DURABILITY, 10) + .put(Enchantment.PROTECTION_EXPLOSIONS, 5) + .put(Enchantment.PROTECTION_FALL, 5) + .put(Enchantment.PROTECTION_FIRE, 5) + .put(Enchantment.THORNS, 1) + .build(); + + final static ImmutableMap BOOTS_MAP = new ImmutableMap.Builder() + .putAll(ARMOR_MAP) + .put(Enchantment.WATER_WORKER, 5) + .put(Enchantment.DEPTH_STRIDER, 3) + .put(Enchantment.FROST_WALKER, 1) + .build(); + + final static ImmutableMap WEAPONS_MAP = new ImmutableMap.Builder() + .put(Enchantment.DAMAGE_ALL, 15) + .put(Enchantment.DURABILITY, 10) + .put(Enchantment.KNOCKBACK, 10) + .put(Enchantment.MENDING, 5) + .put(Enchantment.SWEEPING_EDGE, 5) + .put(Enchantment.FIRE_ASPECT, 1) + .build(); + + final static ImmutableMap TOOLS_MAP = new ImmutableMap.Builder() + .put(Enchantment.DURABILITY, 10) + .put(Enchantment.DIG_SPEED, 10) + .put(Enchantment.SILK_TOUCH, 5) + .put(Enchantment.LOOT_BONUS_BLOCKS, 5) + .put(Enchantment.LOOT_BONUS_MOBS, 5) + .put(Enchantment.LUCK, 1) + .build(); + + final static ImmutableMap BOWS_MAP = new ImmutableMap.Builder() + .put(Enchantment.ARROW_DAMAGE, 10) + .put(Enchantment.ARROW_KNOCKBACK, 5) + .put(Enchantment.ARROW_FIRE, 1) + .build(); + + final static Map FISHING_MAP = new ImmutableMap.Builder() + .put(Enchantment.KNOCKBACK, 3) + .put(Enchantment.LURE, 1) + .build(); + + final static WeightedRandomChooser LEVELS = new ImmutableWeightedRandomChooser<>(LEVELS_MAP); + final static WeightedRandomChooser ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP); + final static WeightedRandomChooser BOOTS = new ImmutableWeightedRandomChooser<>(BOOTS_MAP); + final static WeightedRandomChooser WEAPONS = new ImmutableWeightedRandomChooser<>(WEAPONS_MAP); + final static WeightedRandomChooser TOOLS = new ImmutableWeightedRandomChooser<>(TOOLS_MAP); + final static WeightedRandomChooser BOWS = new ImmutableWeightedRandomChooser<>(BOWS_MAP); + final static WeightedRandomChooser FISHING = new ImmutableWeightedRandomChooser<>(FISHING_MAP); + + Map>> savedEnchantments; + + public EnchantmentMutation(Match match) { + super(match, true); + this.savedEnchantments = new WeakHashMap<>(); + } + + public void apply(ItemStack item, EntityEquipment equipment) { + // Pick the enchantment chooser depending on the item's material + WeightedRandomChooser chooser; + if(item == null || ItemUtils.isNothing(item)) { + return; + } else if(ItemUtils.isWeapon(item)) { + chooser = WEAPONS; + } else if(ItemUtils.isArmor(item)) { + if(equipment.getBoots().equals(item)) { + chooser = BOOTS; + } else { + chooser = ARMOR; + } + } else if(ItemUtils.isTool(item)) { + chooser = TOOLS; + } else if(Material.FISHING_ROD.equals(item.getType())) { + chooser = FISHING; + } else if(Material.BOW.equals(item.getType())) { + chooser = BOWS; + } else { + chooser = null; + } + if(chooser != null) { + // Save the item's enchantments if they need to be restored + Entity entity = equipment.getHolder(); + Map> byEntity = savedEnchantments.getOrDefault(entity, new WeakHashMap<>()); + byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments())); + savedEnchantments.put(entity, byEntity); + // Apply the new enchantments + int amountOfEnchants = LEVELS.choose(entropy); + for(int i = 0; i < amountOfEnchants; i++) { + item.addUnsafeEnchantment(chooser.choose(entropy), LEVELS.choose(entropy)); + } + + } + } + + @Override + public void apply(MatchPlayer player) { + super.apply(player); + player.getInventory().forEach(item -> { + // Random number of enchantments on each item + int numberOfEnchants = LEVELS.choose(entropy); + for(int i = 0; i < numberOfEnchants; i++) { + apply(item, player.getBukkit().getEquipment()); + } + }); + } + + @Override + public void remove(MatchPlayer player) { + super.remove(player); + remove(player.getBukkit()); + } + + public void remove(Entity entity) { + savedEnchantments.getOrDefault(entity, new HashMap<>()).forEach((ItemStack item, Map old) -> { + item.getEnchantments().keySet().forEach(item::removeEnchantment); // Clear the current enchantments + item.addUnsafeEnchantments(old); // Add the old enchantments back + }); + savedEnchantments.remove(entity); + } + + @Override + public void disable() { + ImmutableSet.copyOf(savedEnchantments.keySet()).forEach(this::remove); + savedEnchantments.clear(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java new file mode 100644 index 0000000..61d4922 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java @@ -0,0 +1,150 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.ImmutableMap; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerSpawnEntityEvent; +import org.bukkit.inventory.HorseInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.events.MatchPlayerDeathEvent; +import tc.oc.pgm.events.PlayerChangePartyEvent; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +public class EquestrianMutation extends KitMutation { + + final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() + .put(EntityType.HORSE, 100) + // FIXME: Saddle do not work on these horses + //.put(EntityType.SKELETON_HORSE, 5) + //.put(EntityType.ZOMBIE_HORSE, 5) + //.put(EntityType.LLAMA, 1) + .build(); + + final static ImmutableMap ARMOR_MAP = new ImmutableMap.Builder() + .put(Material.SADDLE, 25) + .put(Material.GOLD_BARDING, 10) + .put(Material.IRON_BARDING, 5) + .put(Material.DIAMOND_BARDING, 1) + .build(); + + final static WeightedRandomChooser TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP); + final static WeightedRandomChooser ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP); + + final Map horses; + + public EquestrianMutation(Match match) { + super(match, false); + this.horses = new WeakHashMap<>(); + } + + @Override + public void disable() { + super.disable(); + match.participants().forEach(this::remove); + horses.clear(); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + Location location = player.getLocation(); + // If there is not enough room to spawn a horse, give the player + // an egg so they can spawn it later + if(!spawnable(location)) { + ItemStack item = item(Material.MONSTER_EGG); + SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); + egg.setSpawnedType(TYPES.choose(entropy)); + item.setItemMeta(egg); + kits.add(new FreeItemKit(item)); + } + } + + @Override + public void apply(MatchPlayer player) { + super.apply(player); + Location location = player.getLocation(); + if(spawnable(location)) { + setup(player, spawn(location, (Class) TYPES.choose(match.entropyForTick()).getEntityClass())); + } + } + + @Override + public void remove(MatchPlayer player) { + super.remove(player); + AbstractHorse horse = horses.remove(player); + if(horse != null) { + horse.ejectAll(); + horse.remove(); + } + } + + public void setup(MatchPlayer player, AbstractHorse horse) { + horses.put(player, horse); + horse.setAdult(); + horse.setJumpStrength(2 * entropy.randomDouble()); + horse.setDomestication(1); + horse.setMaxDomestication(1); + horse.setTamed(true); + horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0)); + horse.setOwner(player.getBukkit()); + horse.setPassenger(player.getBukkit()); + if(horse instanceof Horse) { + Horse horsey = (Horse) horse; + horsey.setStyle(entropy.randomElement(Horse.Style.values())); + horsey.setColor(entropy.randomElement(Horse.Color.values())); + HorseInventory inventory = horsey.getInventory(); + inventory.setSaddle(item(Material.SADDLE)); + inventory.setArmor(item(ARMOR.choose(entropy))); + } + } + + public boolean spawnable(Location location) { + // Allow at least 4 blocks of air from the feet of the player + // to allow a horse to be spawned + for(int i = 0; i <= 4; i++) { + if(!location.clone().add(0, i, 0).getBlock().isEmpty()) { + return false; + } + } + return true; + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEntitySpawn(PlayerSpawnEntityEvent event) { + Entity entity = event.getEntity(); + if(TYPE_MAP.containsKey(entity.getType())) { + match.participant(event.getPlayer()) + .ifPresent(player -> setup(player, (AbstractHorse) event.getEntity())); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onPlayerDeath(MatchPlayerDeathEvent event) { + remove(event.getVictim()); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onPartyChange(PlayerChangePartyEvent event) { + remove(event.getPlayer()); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java new file mode 100644 index 0000000..d71e969 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java @@ -0,0 +1,70 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.Range; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.inventory.PlayerInventory; +import tc.oc.commons.bukkit.item.ItemBuilder; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.pgm.killreward.KillReward; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; + +public class ExplosiveMutation extends KitMutation { + + final static ItemKit TNT = new FreeItemKit(item(Material.TNT, 8)); + final static ItemKit LIGHTER = new FreeItemKit(item(Material.FLINT_AND_STEEL)); + + final static ItemKit FIRE_BOW = new FreeItemKit(new ItemBuilder(item(Material.BOW)).enchant(Enchantment.ARROW_FIRE, 1).get()); + final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 8)); + + final static Range RADIUS = Range.openClosed(0, 4); + + final WeakHashSet tracked; + + public ExplosiveMutation(Match match) { + super(match, false); + this.tracked = new WeakHashSet<>(); + this.rewards.add(new KillReward(TNT)); + this.rewards.add(new KillReward(ARROWS)); + } + + @Override + public void disable() { + super.disable(); + tracked.clear(); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + PlayerInventory inv = player.getInventory(); + if(random.nextBoolean()) { // tnt and lighter kit + if(!inv.contains(Material.TNT)) kits.add(TNT); + if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER); + } else { // fire bow and arrows kit + if(!inv.contains(Material.ARROW)) kits.add(ARROWS); + if(!inv.contains(Material.BOW)) { + kits.add(FIRE_BOW); + } else { + inv.all(Material.BOW).values().forEach(bow -> bow.addUnsafeEnchantments(FIRE_BOW.item().getEnchantments())); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onExplosionPrime(ExplosionPrimeEvent event) { + event.setRadius(event.getRadius() + entropy.randomInt(RADIUS)); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/GlowMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/GlowMutation.java new file mode 100644 index 0000000..dff9ce0 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/GlowMutation.java @@ -0,0 +1,17 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.pgm.kits.PotionKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.mutation.types.KitMutation; + +public class GlowMutation extends KitMutation { + + final static PotionKit GLOW = new PotionKit(new PotionEffect(PotionEffectType.GLOWING, Integer.MAX_VALUE, 0)); + + public GlowMutation(Match match) { + super(match, true, GLOW); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java new file mode 100644 index 0000000..e5e5196 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java @@ -0,0 +1,48 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.gamerules.GameRulesMatchModule; +import tc.oc.pgm.killreward.KillReward; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.shield.ShieldKit; +import tc.oc.pgm.shield.ShieldParameters; + +public class HardcoreMutation extends KitMutation { + + final static String KEY = "naturalRegeneration"; + final static ShieldKit SHIELD = new ShieldKit(new ShieldParameters()); + final static ItemKit APPLE = new FreeItemKit(item(Material.GOLDEN_APPLE)); + + String previous; // Stores the previous game rule setting + + public HardcoreMutation(Match match) { + super(match, false); + this.kits.add(SHIELD); + this.rewards.add(new KillReward(APPLE)); + } + + public GameRulesMatchModule rules() { + return match.module(GameRulesMatchModule.class).get(); + } + + @Override + public void enable() { + super.enable(); + previous = world.getGameRuleValue(KEY); + rules().gameRules().put(KEY, "false"); + } + + @Override + public void disable() { + rules().gameRules().remove(KEY); + if(previous != null) { + world.setGameRuleValue(KEY, previous); + } + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java new file mode 100644 index 0000000..afcdfba --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java @@ -0,0 +1,24 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.killreward.KillReward; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.HealthKit; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.kits.MaxHealthKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.mutation.types.KitMutation; + +public class HealthMutation extends KitMutation { + + final static HealthKit HEALTH = new HealthKit(40); + final static MaxHealthKit MAX_HEALTH = new MaxHealthKit(40); + final static ItemKit APPLE = new FreeItemKit(item(Material.GOLDEN_APPLE, 3)); + + public HealthMutation(Match match) { + super(match, false, MAX_HEALTH, HEALTH, APPLE); + this.rewards.add(new KillReward(APPLE)); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/JumpMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/JumpMutation.java new file mode 100644 index 0000000..489f280 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/JumpMutation.java @@ -0,0 +1,14 @@ +package tc.oc.pgm.mutation.types.kit; + +import tc.oc.pgm.doublejump.DoubleJumpKit; +import tc.oc.pgm.match.Match; + +public class JumpMutation extends NoFallMutation { + + final static DoubleJumpKit JUMP = new DoubleJumpKit(); + + public JumpMutation(Match match) { + super(match, false, JUMP); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java new file mode 100644 index 0000000..9855a17 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java @@ -0,0 +1,56 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Range; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SpawnEggMeta; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; + +public class MobsMutation extends KitMutation { + + final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() + .put(EntityType.ZOMBIE, 50) + .put(EntityType.SKELETON, 40) + .put(EntityType.SPIDER, 40) + .put(EntityType.CREEPER, 30) + .put(EntityType.BLAZE, 20) + .put(EntityType.GHAST, 20) + .put(EntityType.SHULKER, 20) + .put(EntityType.WITCH, 10) + .put(EntityType.ENDERMAN, 10) + .put(EntityType.PIG_ZOMBIE, 5) + .put(EntityType.WITHER_SKELETON, 1) + .build(); + + final static WeightedRandomChooser TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP); + + final static Range AMOUNT = Range.closed(1, 3); + + public MobsMutation(Match match) { + super(match, true); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + int eggs = entropy.randomInt(AMOUNT); + for(int i = 0; i < eggs; i++) { + ItemStack item = item(Material.MONSTER_EGG, entropy.randomInt(AMOUNT)); + SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); + egg.setSpawnedType(TYPES.choose(entropy)); + item.setItemMeta(egg); + kits.add(new FreeItemKit(item)); + } + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java new file mode 100644 index 0000000..eb3363b --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java @@ -0,0 +1,44 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.event.entity.EntityDamageEvent; +import tc.oc.pgm.damage.DisableDamageMatchModule; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.PlayerRelation; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class NoFallMutation extends KitMutation { + + final static EntityDamageEvent.DamageCause FALL = EntityDamageEvent.DamageCause.FALL; + final static Iterable RELATIONS = Stream.of(PlayerRelation.values()).collect(Collectors.toList()); + + Iterable previous; + + public NoFallMutation(Match match, boolean force, Kit... kits) { + super(match, force, kits); + } + + public DisableDamageMatchModule damage() { + return match.module(DisableDamageMatchModule.class).get(); + } + + @Override + public void enable() { + super.enable(); + previous = damage().causes().get(FALL); + damage().causes().putAll(FALL, RELATIONS); + } + + @Override + public void disable() { + damage().causes().removeAll(FALL); + if(previous != null) { + damage().causes().putAll(FALL, previous); + } + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java new file mode 100644 index 0000000..6a51913 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java @@ -0,0 +1,100 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Range; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; + +public class PotionMutation extends KitMutation { + + final static ImmutableMap BAD_MAP = new ImmutableMap.Builder() + .put(PotionEffectType.WEAKNESS, 15) + .put(PotionEffectType.SLOW, 10) + .put(PotionEffectType.POISON, 10) + .put(PotionEffectType.BLINDNESS, 3) + .put(PotionEffectType.LEVITATION, 1) + .build(); + + final static ImmutableMap GOOD_MAP = new ImmutableMap.Builder() + .put(PotionEffectType.SPEED, 15) + .put(PotionEffectType.INCREASE_DAMAGE, 15) + .put(PotionEffectType.DAMAGE_RESISTANCE, 10) + .put(PotionEffectType.REGENERATION, 10) + .put(PotionEffectType.FIRE_RESISTANCE, 10) + .put(PotionEffectType.HEALTH_BOOST, 5) + .put(PotionEffectType.JUMP, 5) + .put(PotionEffectType.INVISIBILITY, 1) + .build(); + + final static ImmutableMap BOTTLE_BAD_MAP = new ImmutableMap.Builder() + .put(Material.SPLASH_POTION, 5) + .put(Material.LINGERING_POTION, 1) + .build(); + + final static ImmutableMap BOTTLE_GOOD_MAP = new ImmutableMap.Builder() + .putAll(BOTTLE_BAD_MAP) + .put(Material.POTION, 10) + .build(); + + + final static WeightedRandomChooser BAD = new ImmutableWeightedRandomChooser<>(BAD_MAP); + final static WeightedRandomChooser GOOD = new ImmutableWeightedRandomChooser<>(GOOD_MAP); + + final static WeightedRandomChooser BAD_BOTTLE = new ImmutableWeightedRandomChooser<>(BOTTLE_BAD_MAP); + final static WeightedRandomChooser GOOD_BOTTLE = new ImmutableWeightedRandomChooser<>(BOTTLE_GOOD_MAP); + + final static Range BAD_DURATION_RANGE = Range.closed(3, 10); + final static Range GOOD_DURATION_RANGE = Range.closed(10, 45); + + final static Range AMOUNT_RANGE = Range.closed(1, 3); + final static Range AMPLIFIER_RANGE = Range.closed(0, 2); + + public PotionMutation(Match match) { + super(match, false); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + int numberOfPotions = entropy.randomInt(AMOUNT_RANGE); + for(int i = 0; i < numberOfPotions; i++) { + WeightedRandomChooser type; + WeightedRandomChooser material; + Range range; + // Determine whether the potion will be "good" or "bad" + if(random.nextBoolean()) { + type = BAD; + material = BAD_BOTTLE; + range = BAD_DURATION_RANGE; + } else { + type = GOOD; + material = GOOD_BOTTLE; + range = GOOD_DURATION_RANGE; + } + // Choose all the random attributes + PotionEffectType effect = type.choose(entropy); + Material bottle = material.choose(entropy); + int duration = 20 * entropy.randomInt(range); + int amplifier = entropy.randomInt(AMPLIFIER_RANGE); + // Apply the attributes to the item stack + ItemStack potion = item(bottle); + PotionMeta meta = (PotionMeta) potion.getItemMeta(); + meta.addCustomEffect(new PotionEffect(effect, duration, amplifier), true); + potion.setItemMeta(meta); + kits.add(new FreeItemKit(potion)); + } + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java new file mode 100644 index 0000000..60cf9cc --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java @@ -0,0 +1,84 @@ +package tc.oc.pgm.mutation.types.kit; + +import com.google.common.collect.Range; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.TippedArrow; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.PGM; +import tc.oc.pgm.killreward.KillReward; +import tc.oc.pgm.kits.FreeItemKit; +import tc.oc.pgm.kits.ItemKit; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.List; + +public class ProjectileMutation extends KitMutation { + + final static WeightedRandomChooser ENCHANTMENTS = new ImmutableWeightedRandomChooser<>(EnchantmentMutation.BOWS_MAP); + final static WeightedRandomChooser POTIONS = new ImmutableWeightedRandomChooser<>(PotionMutation.BAD_MAP); + + final static Range ENCHANT_RANGE = Range.closed(1, 3); + final static Range AMPLIFIER_RANGE = Range.closed(0, 3); + final static Range DURATION_RANGE = Range.closed(3, 10); + + final static ItemKit BOW = new FreeItemKit(item(Material.BOW)); + final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16)); + + final static String KEY = "is_modified_arrow"; + + public ProjectileMutation(Match match) { + super(match, false); + this.rewards.add(new KillReward(ARROWS)); + } + + @Override + public void apply(MatchPlayer player) { + super.apply(player); + Inventory inventory = player.getInventory(); + inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy), entropy.randomInt(ENCHANT_RANGE))); + } + + @Override + public void kits(MatchPlayer player, List kits) { + super.kits(player, kits); + Inventory inventory = player.getInventory(); + if(!inventory.contains(Material.BOW)) kits.add(BOW); + if(!inventory.contains(Material.ARROW)) kits.add(ARROWS); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onBowShoot(EntityShootBowEvent event) { + Entity projectile = event.getProjectile(); + if(projectile instanceof Arrow && (!projectile.hasMetadata(KEY) || !projectile.getMetadata(KEY, PGM.get()).asBoolean())) { + Arrow arrow = (Arrow) projectile; + TippedArrow tipped = world.spawn(projectile.getLocation(), TippedArrow.class); + tipped.setMetadata(KEY, new FixedMetadataValue(PGM.get(), true)); + tipped.setCritical(arrow.isCritical()); + tipped.setKnockbackStrength(arrow.getKnockbackStrength()); + tipped.setDamage(arrow.getDamage()); + tipped.setShooter(arrow.getShooter()); + tipped.setVelocity(projectile.getVelocity()); + tipped.setPickupRule(Arrow.PickupRule.DISALLOWED); + tipped.addCustomEffect(new PotionEffect(POTIONS.choose(entropy), 20 * entropy.randomInt(DURATION_RANGE), entropy.randomInt(AMPLIFIER_RANGE)), true); + arrow.remove(); + event.setCancelled(true); + match.callEvent(new ProjectileLaunchEvent(tipped)); + } + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/StealthMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/StealthMutation.java new file mode 100644 index 0000000..9d02ec0 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/StealthMutation.java @@ -0,0 +1,25 @@ +package tc.oc.pgm.mutation.types.kit; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.pgm.kits.PotionKit; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.mutation.types.KitMutation; + +import java.util.stream.Stream; + +public class StealthMutation extends KitMutation { + + final static PotionKit INVISIBILITY = new PotionKit(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0)); + + public StealthMutation(Match match) { + super(match, true, INVISIBILITY); + } + + @Override + public Stream saved() { + return Slot.Armor.armor(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java new file mode 100644 index 0000000..66988d2 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java @@ -0,0 +1,29 @@ +package tc.oc.pgm.mutation.types.other; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.mutation.types.MutationModule; +import tc.oc.pgm.rage.RageMatchModule; + +public class RageMutation extends MutationModule { + + RageMatchModule rage; + + public RageMutation(Match match) { + super(match); + this.rage = match.module(RageMatchModule.class).orElse(new RageMatchModule(match)); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerDamage(EntityDamageByEntityEvent event) { + rage.handlePlayerDamage(event); + } + + @Override + public void disable() { + super.disable(); + rage = null; + } +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java new file mode 100644 index 0000000..7733a81 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java @@ -0,0 +1,315 @@ +package tc.oc.pgm.mutation.types.targetable; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Range; +import org.apache.commons.lang.math.Fraction; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.PigZombie; +import org.bukkit.entity.Slime; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.entity.Zombie; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; +import tc.oc.commons.core.random.WeightedRandomChooser; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.kit.EnchantmentMutation; +import tc.oc.pgm.mutation.types.TargetMutation; +import tc.oc.pgm.points.PointProviderAttributes; +import tc.oc.pgm.points.RandomPointProvider; +import tc.oc.pgm.points.RegionPointProvider; +import tc.oc.pgm.regions.CuboidRegion; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; + +import static tc.oc.commons.core.random.RandomUtils.nextBoolean; + +public class ApocalypseMutation extends TargetMutation { + + final static ImmutableMap AMOUNT_MAP = new ImmutableMap.Builder() + .put(3, 25) + .put(5, 20) + .put(10, 15) + .put(20, 5) + .put(50, 1) + .build(); + + final static ImmutableMap STACK_MAP = new ImmutableMap.Builder() + .put(1, 100) + .put(2, 25) + .build(); + + final static ImmutableMap AERIAL_MAP = new ImmutableMap.Builder() + .put(EntityType.VEX, 5) + .put(EntityType.BLAZE, 1) + .build(); + + final static ImmutableMap GROUND_MAP = new ImmutableMap.Builder() + .put(EntityType.SPIDER, 50) + .put(EntityType.ZOMBIE, 40) + .put(EntityType.CREEPER, 30) + .put(EntityType.HUSK, 20) + .put(EntityType.CAVE_SPIDER, 10) + .put(EntityType.PIG_ZOMBIE, 1) + .build(); + + final static ImmutableMap RANGED_MAP = new ImmutableMap.Builder() + .put(EntityType.SKELETON, 50) + .put(EntityType.STRAY, 20) + .put(EntityType.BLAZE, 20) + .put(EntityType.GHAST, 10) + .put(EntityType.SHULKER, 5) + .put(EntityType.WITCH, 5) + .put(EntityType.WITHER_SKELETON, 1) + .build(); + + final static ImmutableMap FLYABLE_MAP = new ImmutableMap.Builder() + .putAll(AERIAL_MAP) + .put(EntityType.BAT, 10) + .build(); + + final static ImmutableMap PASSENGER_MAP = new ImmutableMap.Builder() + .putAll(RANGED_MAP) + .put(EntityType.CREEPER, 40) + .put(EntityType.PRIMED_TNT, 1) + .build(); + + final static ImmutableMap CUBE_MAP = new ImmutableMap.Builder() + .put(EntityType.SLIME, 10) + .put(EntityType.MAGMA_CUBE, 1) + .build(); + + final static WeightedRandomChooser AMOUNT = new ImmutableWeightedRandomChooser<>(AMOUNT_MAP); + final static WeightedRandomChooser STACK = new ImmutableWeightedRandomChooser<>(STACK_MAP); + final static WeightedRandomChooser AERIAL = new ImmutableWeightedRandomChooser<>(AERIAL_MAP); + final static WeightedRandomChooser GROUND = new ImmutableWeightedRandomChooser<>(GROUND_MAP); + final static WeightedRandomChooser RANGED = new ImmutableWeightedRandomChooser<>(RANGED_MAP); + final static WeightedRandomChooser FLYABLE = new ImmutableWeightedRandomChooser<>(FLYABLE_MAP); + final static WeightedRandomChooser PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP); + final static WeightedRandomChooser CUBE = new ImmutableWeightedRandomChooser<>(CUBE_MAP); + + final static int DISTANCE = 15; // Max distance entities spawn from players + final static int PARTICIPANT_ENTITIES = 25; // Max entities on the field per participant + final static int MAX_ENTITIES = 500; // Max total entities on the field + final static Range AIR_OFFSET = Range.closed(DISTANCE / 4, DISTANCE); // Y-axis offset for spawning flying entities + final static Fraction SPECIAL_CHANCE = Fraction.ONE_FIFTH; // Chance of a special attribute occuring in an entity + final static int SPECIAL_MULTIPLIER = 3; // Multiplier for special attributes + + final WeakHashMap entities; + long time; + + public ApocalypseMutation(Match match) { + super(match, Duration.ofSeconds(20)); + this.entities = new WeakHashMap<>(); + } + + /** + * Get the maximum amount of entities that can be spawned. + */ + public int entitiesMax() { + return Math.min((int) match.participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES); + } + + /** + * Get the number of available slots are left for additional entities to spawn. + */ + public int entitiesLeft() { + return entitiesMax() - world.getLivingEntities().size() + (int) match.participants().count(); + } + + /** + * Generate a random spawn point given two locations. + */ + public Optional location(Location start, Location end) { + return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), new PointProviderAttributes()))).getPoint(match, null)); + } + + /** + * Spawn a cohort of entities at the given location. + * @param location location to spawn the entity. + * @param ground whether the location is on the ground. + */ + public void spawn(Location location, boolean ground) { + int slots = entitiesLeft(); + int queued = AMOUNT.choose(entropy); + // Remove any entities that may be over the max limit + despawn(queued - slots); + // Determine whether the entities should be airborn + int stack = STACK.choose(entropy); + boolean air = !ground || nextBoolean(random, SPECIAL_CHANCE); + if(air) { + stack += (stack == 1 && random.nextBoolean() ? 1 : 0); + location.add(0, entropy.randomInt(AIR_OFFSET), 0); + } + // Select the random entity chooser based on ground, air, and stacked + boolean stacked = stack > 1; + WeightedRandomChooser chooser; + if(air) { + if(stacked) { + if(ground) { + chooser = nextBoolean(random, SPECIAL_CHANCE) ? CUBE : FLYABLE; + } else { + chooser = FLYABLE; + } + } else { + chooser = AERIAL; + } + } else { + if(stacked) { + chooser = GROUND; + } else { + chooser = random.nextBoolean() ? GROUND : RANGED; + } + } + // Select the specific entity types for the spawn, + // all entities will have the same sequence of entity type + // but may have variations (like armor) between them. + List types = new ArrayList<>(); + for(int i = 0; i < stack; i++) { + types.add((i == 0 ? chooser : PASSENGER).choose(entropy)); + } + // Spawn the mobs and stack them if required + for(int i = 0; i < queued; i++) { + Entity last = null; + for(EntityType type : types) { + Entity entity = spawn(location, type); + if(last != null) { + last.setPassenger(entity); + } + last = entity; + } + } + + } + + /** + * Spawn an individual entitiy at a location given an entity type. + * @param location the location to spawn at. + * @param type the type of entity. + * @return the constructed entity. + */ + public LivingEntity spawn(Location location, EntityType type) { + EnchantmentMutation enchant = new EnchantmentMutation(match); + LivingEntity entity = (LivingEntity) spawn(location, type.getEntityClass()); + EntityEquipment equipment = entity.getEquipment(); + entity.setVelocity(Vector.getRandom()); + entity.setAbsorption(5); + ItemStack held = null; + switch(type) { + case SKELETON: + case WITHER_SKELETON: + case STRAY: + held = item(Material.BOW); + break; + case ZOMBIE: + case ZOMBIE_VILLAGER: + case HUSK: + Zombie zombie = (Zombie) entity; + zombie.setBaby(nextBoolean(random, SPECIAL_CHANCE)); + break; + case PIG_ZOMBIE: + PigZombie pigZombie = (PigZombie) entity; + pigZombie.setAngry(true); + pigZombie.setAnger(Integer.MAX_VALUE); + held = item(Material.GOLD_SWORD); + break; + case CREEPER: + Creeper creeper = (Creeper) entity; + creeper.setPowered(nextBoolean(random, SPECIAL_CHANCE)); + world.strikeLightningEffect(location); + break; + case PRIMED_TNT: + TNTPrimed tnt = (TNTPrimed) entity; + tnt.setFuseTicks(tnt.getFuseTicks() * SPECIAL_MULTIPLIER); + break; + case SLIME: + case MAGMA_CUBE: + Slime slime = (Slime) entity; + slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER); + break; + case SKELETON_HORSE: + world.strikeLightning(location); + break; + } + if(held != null && random.nextBoolean()) { + enchant.apply(held, equipment); + equipment.setItemInMainHand(held); + } + entities.put(entity, Instant.now()); + return entity; + } + + /** + * Select the entities that have lived the longest and remove them + * to make room for new entities. + * @param amount the amount of entities to despawn. + */ + public void despawn(int amount) { + entities.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue(Comparator.comparing(Instant::toEpochMilli))) + .limit(Math.max(0, amount)) + .map(Map.Entry::getKey) + .forEach(Entity::remove); + } + + @Override + public void execute(List players) { + // At least one player is required to spawn mobs + if(players.size() >= 1) { + Location start, end; + start = players.get(0).getLocation(); // player 1 is the first location + if(players.size() >= 2) { + end = players.get(1).getLocation(); // if player 2 exists, they are the second location + } else { // if no player 2, generate a random location near player 1 + end = start.clone().add(Vector.getRandom().multiply(DISTANCE)); + } + Optional location = location(start, end); + if(location.isPresent()) { // if the location is safe (on ground) + spawn(location.get(), true); + } else { // if the location was not safe, generate a simple midpoint location + spawn(start.position().midpoint(end.position()).toLocation(world), false); + } + } + } + + @Override + public int targets() { + return 2; // Always require 2 targets to generate a spawn location between them + } + + @Override + public void enable() { + super.enable(); + time = world.getTime(); + } + + @Repeatable + public void tick() { + world.setTime(16000); // Night time to prevent flaming entities + } + + @Override + public void disable() { + world.setTime(time); + despawn(entities.size()); + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java new file mode 100644 index 0000000..6cd8b40 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java @@ -0,0 +1,66 @@ +package tc.oc.pgm.mutation.types.targetable; + +import com.google.common.collect.Range; +import org.bukkit.Location; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.util.Vector; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.TargetMutation; + +import java.time.Duration; +import java.util.List; + +public class BomberMutation extends TargetMutation { + + final static Duration FREQUENCY = Duration.ofSeconds(30); + final static Range TARGETS = Range.closed(1, 5); + final static Range HEIGHT = Range.closed(30, 60); + final static Range TICKS = Range.closed(10, 30); + + final WeakHashSet falling; + + public BomberMutation(Match match) { + super(match, FREQUENCY); + this.falling = new WeakHashSet<>(); + } + + @Override + public void execute(List players) { + players.forEach(player -> { + int bombs = entropy.randomInt(TARGETS); + int height = entropy.randomInt(HEIGHT); + Location location = player.getLocation().clone().add(0, height, 0); + for(int i = 0; i < bombs; i++) { + TNTPrimed tnt = world.spawn(location, TNTPrimed.class); + tnt.setGlowing(true); + tnt.setIsIncendiary(false); + tnt.setFuseTicks(Integer.MAX_VALUE); + tnt.setVelocity( + new Vector( + (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble(), + -entropy.randomDouble(), + (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble() + ) + ); + falling.add(tnt); + } + }); + } + + @Override + public int targets() { + return match.entropyForTick().randomInt(TARGETS); + } + + @Repeatable + public void tick() { + falling.stream() + .filter(TNTPrimed::isOnGround) + .forEach(tnt -> tnt.setFuseTicks(entropy.randomInt(TICKS))); + falling.removeIf(TNTPrimed::isOnGround); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java new file mode 100644 index 0000000..583d931 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java @@ -0,0 +1,42 @@ +package tc.oc.pgm.mutation.types.targetable; + +import com.google.common.collect.Range; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.util.Vector; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.mutation.types.TargetMutation; + +import java.time.Duration; +import java.util.List; + +public class LightningMutation extends TargetMutation { + + final static Duration FREQUENCY = Duration.ofSeconds(30); + final static Range TARGETS = Range.closed(1, 5); + final static Range STRIKES = Range.closed(0, 3); + + public LightningMutation(Match match) { + super(match, FREQUENCY); + } + + @Override + public void execute(List players) { + players.forEach(player -> { + World world = match.getWorld(); + Location location = player.getLocation(); + world.strikeLightning(location.clone().add(Vector.getRandom())); + int strikes = match.entropyForTick().randomInt(STRIKES); + for(int i = 0; i < strikes; i++) { + world.strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2)))); + } + }); + } + + @Override + public int targets() { + return match.entropyForTick().randomInt(TARGETS); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/shield/ShieldParameters.java b/PGM/src/main/java/tc/oc/pgm/shield/ShieldParameters.java index 2b3d132..8f03821 100644 --- a/PGM/src/main/java/tc/oc/pgm/shield/ShieldParameters.java +++ b/PGM/src/main/java/tc/oc/pgm/shield/ShieldParameters.java @@ -14,4 +14,8 @@ public class ShieldParameters { this.maxHealth = maxHealth; this.rechargeDelay = rechargeDelay; } + + public ShieldParameters() { + this(DEFAULT_HEALTH, DEFAULT_DELAY); + } } diff --git a/PGM/src/main/java/tc/oc/pgm/spawns/states/Alive.java b/PGM/src/main/java/tc/oc/pgm/spawns/states/Alive.java index 4c1851a..6aad31d 100644 --- a/PGM/src/main/java/tc/oc/pgm/spawns/states/Alive.java +++ b/PGM/src/main/java/tc/oc/pgm/spawns/states/Alive.java @@ -21,8 +21,7 @@ import tc.oc.pgm.match.Competitor; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.ParticipantState; import tc.oc.pgm.mutation.MutationMatchModule; -import tc.oc.pgm.mutation.submodule.KitMutationModule; -import tc.oc.pgm.mutation.submodule.MutationModule; +import tc.oc.pgm.mutation.types.KitMutation; import tc.oc.pgm.spawns.Spawn; import tc.oc.pgm.spawns.events.ParticipantDespawnEvent; import tc.oc.pgm.spawns.events.ParticipantReleaseEvent; @@ -86,16 +85,10 @@ public class Alive extends Participating { match.module(KillRewardMatchModule.class).ifPresent(krmm -> krmm.giveDeadPlayerRewards(player)); // Apply kit injections from KitMutationModules - match.module(MutationMatchModule.class).ifPresent(mmm -> { - for(MutationModule module : mmm.getMutationModules()) { - if(module instanceof KitMutationModule) { - KitMutationModule kitModule = ((KitMutationModule) module); - for(Kit kit : kitModule.getKits()) { - player.facet(KitPlayerFacet.class).applyKit(kit, kitModule.isForceful()); - } - } - } - }); + match.module(MutationMatchModule.class) + .ifPresent(mmm -> mmm.mutationModules().stream() + .filter(mm -> mm instanceof KitMutation) + .forEach(mm -> ((KitMutation) mm).apply(player))); player.getBukkit().updateInventory(); diff --git a/PGM/src/test/java/tc/oc/pgm/mutation/MutationTest.java b/PGM/src/test/java/tc/oc/pgm/mutation/MutationTest.java deleted file mode 100644 index 7de5b55..0000000 --- a/PGM/src/test/java/tc/oc/pgm/mutation/MutationTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package tc.oc.pgm.mutation; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import tc.oc.api.docs.virtual.MatchDoc; - -@RunWith(JUnit4.class) -public class MutationTest { - - @Test - public void testMutationEnumsAreSynced() { - assertTrue("Mutation enums must be equal in size", Mutation.values().length == MatchDoc.Mutation.values().length); - for(int i = 0; i < Mutation.values().length; i++) { - assertTrue("Mutation enums of ordinal (" + i + ") do not match", Mutation.values()[i].name().equals(MatchDoc.Mutation.values()[i].name())); - } - } - -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemBuilder.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemBuilder.java index fc8ff0c..29c5bdf 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemBuilder.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemBuilder.java @@ -123,7 +123,7 @@ public class ItemBuilder> { */ public S lore(String lore) { meta().setLore(meta().hasLore() ? ListUtils.append(meta().getLore(), lore) - : Collections.singletonList(lore)); + : Collections.singletonList(lore)); return self(); } @@ -172,4 +172,14 @@ public class ItemBuilder> { meta(SkullMeta.class).setOwner(name, uuid, skin); return self(); } + + public S shareable(boolean yes) { + new BooleanItemTag("prevent-sharing", false).set(stack, !yes); + return self(); + } + + public S locked(boolean yes) { + new BooleanItemTag("locked", false).set(stack, !yes); + return self(); + } } diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemUtils.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemUtils.java index 1e40358..7c38ef4 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemUtils.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/item/ItemUtils.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import javax.annotation.Nullable; import com.google.common.collect.ImmutableSet; @@ -100,6 +101,24 @@ public class ItemUtils { return isNothing(stack) ? Optional.empty() : Optional.of(stack); } + private static final String[] TOOLS = {"axe", "hoe", "spade"}; + + public static boolean isTool(ItemStack stack) { + return isTool(stack.getData()); + } + + public static boolean isTool(MaterialData item) { + return Stream.of(TOOLS).anyMatch(query -> item.getItemType().name().toLowerCase().contains(query)); + } + + public static boolean isArmor(ItemStack stack) { + return isArmor(stack.getData()); + } + + public static boolean isArmor(MaterialData item) { + return !Bukkit.getItemFactory().getAttributeModifiers(item, Attribute.GENERIC_ARMOR).isEmpty(); + } + public static boolean isWeapon(ItemStack stack) { return isWeapon(stack.getData()); } diff --git a/Util/core/src/main/java/tc/oc/commons/core/collection/ForwardingSet.java b/Util/core/src/main/java/tc/oc/commons/core/collection/ForwardingSet.java new file mode 100644 index 0000000..0371afb --- /dev/null +++ b/Util/core/src/main/java/tc/oc/commons/core/collection/ForwardingSet.java @@ -0,0 +1,91 @@ +package tc.oc.commons.core.collection; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class ForwardingSet implements Set { + + protected final Map backend; + + public ForwardingSet(Map backend) { + this.backend = backend; + } + + @Override + public int size() { + return backend.size(); + } + + @Override + public boolean isEmpty() { + return backend.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return backend.containsKey(o); + } + + @Override + public Iterator iterator() { + return backend.keySet().iterator(); + } + + @Override + public Object[] toArray() { + return backend.keySet().toArray(); + } + + @Override + public T[] toArray(T[] a) { + return backend.keySet().toArray(a); + } + + @Override + public boolean add(E e) { + if(backend.containsKey(e)) { + return false; + } else { + backend.put(e, null); + return true; + } + } + + @Override + public boolean remove(Object o) { + if(!backend.containsKey(o)) { + return false; + } else { + backend.remove(o); + return true; + } + } + + @Override + public boolean containsAll(Collection c) { + return c.stream().allMatch(this::contains); + } + + @Override + public boolean addAll(Collection c) { + return c.stream().anyMatch(this::add); + } + + @Override + public boolean retainAll(Collection c) { + return c.stream().filter(i -> !contains(i)).anyMatch(this::remove); + } + + @Override + public boolean removeAll(Collection c) { + return c.stream().anyMatch(this::remove); + } + + @Override + public void clear() { + backend.clear(); + } + +} diff --git a/Util/core/src/main/java/tc/oc/commons/core/collection/WeakHashSet.java b/Util/core/src/main/java/tc/oc/commons/core/collection/WeakHashSet.java new file mode 100644 index 0000000..6cd6084 --- /dev/null +++ b/Util/core/src/main/java/tc/oc/commons/core/collection/WeakHashSet.java @@ -0,0 +1,17 @@ +package tc.oc.commons.core.collection; + +import java.util.Collection; +import java.util.WeakHashMap; + +public class WeakHashSet extends ForwardingSet { + + public WeakHashSet() { + super(new WeakHashMap<>()); + } + + public WeakHashSet(Collection initial) { + this(); + this.addAll(initial); + } + +} diff --git a/Util/core/src/main/java/tc/oc/commons/core/random/Entropy.java b/Util/core/src/main/java/tc/oc/commons/core/random/Entropy.java index b172658..3640852 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/random/Entropy.java +++ b/Util/core/src/main/java/tc/oc/commons/core/random/Entropy.java @@ -3,6 +3,7 @@ package tc.oc.commons.core.random; import java.util.List; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Range; import tc.oc.commons.core.util.Ranges; @@ -26,6 +27,10 @@ public interface Entropy { return min + (int) ((randomLong() & 0xffffffffL) * delta / 0x100000000L); } + default T randomElement(T... array) { + return randomElement(Lists.newArrayList(array)); + } + default T randomElement(Iterable iterable) { return Iterables.get(iterable, randomInt(Range.closedOpen(0, Iterables.size(iterable)))); } diff --git a/Util/core/src/main/java/tc/oc/commons/core/random/RandomUtils.java b/Util/core/src/main/java/tc/oc/commons/core/random/RandomUtils.java index caf9983..0859be3 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/random/RandomUtils.java +++ b/Util/core/src/main/java/tc/oc/commons/core/random/RandomUtils.java @@ -3,6 +3,7 @@ package tc.oc.commons.core.random; import java.util.Random; import com.google.common.collect.Iterables; +import org.apache.commons.lang.math.Fraction; public class RandomUtils { @@ -13,4 +14,8 @@ public class RandomUtils { public static T element(Random random, Iterable collection) { return Iterables.get(collection, safeNextInt(random, Iterables.size(collection))); } + + public static boolean nextBoolean(Random random, Fraction chance) { + return random.nextDouble() < chance.doubleValue(); + } } From 88a250b2bb3af34fde8369496bffdd74f111a18a Mon Sep 17 00:00:00 2001 From: Electroid Date: Fri, 31 Mar 2017 16:22:12 -0700 Subject: [PATCH 009/252] Blitz overhaul --- .../commons/bukkit/commands/CommandUtils.java | 26 +- .../i18n/templates/commons/Commons.properties | 1 + .../i18n/templates/pgm/PGMMessages.properties | 21 ++ .../main/i18n/templates/pgm/PGMUI.properties | 6 - .../java/tc/oc/pgm/MapModulesManifest.java | 4 +- .../java/tc/oc/pgm/PGMModulesManifest.java | 2 + .../java/tc/oc/pgm/api/MatchDocument.java | 8 +- .../pgm/api/MatchPublishingMatchModule.java | 16 +- .../java/tc/oc/pgm/blitz/BlitzConfig.java | 31 -- .../main/java/tc/oc/pgm/blitz/BlitzEvent.java | 34 +++ .../java/tc/oc/pgm/blitz/BlitzManifest.java | 17 ++ .../tc/oc/pgm/blitz/BlitzMatchModule.java | 267 +++++------------ .../tc/oc/pgm/blitz/BlitzMatchModuleImpl.java | 283 ++++++++++++++++++ .../tc/oc/pgm/blitz/BlitzMatchResult.java | 2 + .../java/tc/oc/pgm/blitz/BlitzModule.java | 69 ++--- .../java/tc/oc/pgm/blitz/BlitzParser.java | 72 +++++ .../java/tc/oc/pgm/blitz/BlitzProperties.java | 64 ++++ .../oc/pgm/blitz/BlitzVictoryCondition.java | 22 ++ .../java/tc/oc/pgm/blitz/LifeManager.java | 45 --- PGM/src/main/java/tc/oc/pgm/blitz/Lives.java | 73 +++++ .../main/java/tc/oc/pgm/blitz/LivesBase.java | 113 +++++++ .../main/java/tc/oc/pgm/blitz/LivesEvent.java | 32 ++ .../java/tc/oc/pgm/blitz/LivesIndividual.java | 46 +++ .../main/java/tc/oc/pgm/blitz/LivesKit.java | 20 ++ .../main/java/tc/oc/pgm/blitz/LivesTeam.java | 39 +++ .../oc/pgm/countdowns/CountdownContext.java | 4 + .../ghostsquadron/GhostSquadronModule.java | 2 +- .../inventory/ViewInventoryMatchModule.java | 10 +- .../tc/oc/pgm/kits/KitDefinitionParser.java | 6 + .../pgm/mapratings/MapRatingsMatchModule.java | 7 +- .../java/tc/oc/pgm/mutation/Mutation.java | 3 +- .../mutation/types/other/BlitzMutation.java | 47 +++ .../tc/oc/pgm/picker/PickerMatchModule.java | 17 +- .../main/java/tc/oc/pgm/rage/RageModule.java | 2 +- .../java/tc/oc/pgm/score/ScoreModule.java | 4 +- .../oc/pgm/scoreboard/SidebarMatchModule.java | 37 ++- .../java/tc/oc/pgm/teams/TeamFactory.java | 6 + .../anxuiz/tourney/listener/TeamListener.java | 5 +- .../commons/core/formatting/StringUtils.java | 4 + 39 files changed, 1103 insertions(+), 364 deletions(-) delete mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzConfig.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzEvent.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzManifest.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzParser.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzProperties.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/BlitzVictoryCondition.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LifeManager.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/Lives.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LivesEvent.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LivesIndividual.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LivesKit.java create mode 100644 PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java index dda3716..10de6e3 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java @@ -124,14 +124,14 @@ public abstract class CommandUtils { } public static Duration getDuration(CommandContext args, int index, Duration def) throws CommandException { - return args.argsLength() > index ? getDuration(args.getString(index), null) : def; + return getDuration(args.getString(index, null), def); } - public static @Nullable Duration getDuration(@Nullable String text) throws CommandException { + public static @Nullable Duration getDuration(String text) throws CommandException { return getDuration(text, null); } - public static Duration getDuration(@Nullable String text, Duration def) throws CommandException { + public static Duration getDuration(String text, Duration def) throws CommandException { if(text == null) { return def; } else { @@ -143,6 +143,26 @@ public abstract class CommandUtils { } } + public static @Nullable > E getEnum(CommandContext args, CommandSender sender, int index, Class type) throws CommandException { + return getEnum(args, sender, index, type, null); + } + + public static > E getEnum(CommandContext args, CommandSender sender, int index, Class type, E def) throws CommandException { + return getEnum(args.getString(index, null), sender, type, def); + } + + public static > E getEnum(String text, CommandSender sender, Class type, E def) throws CommandException { + if(text == null) { + return def; + } else { + try { + return Enum.valueOf(type, text.toUpperCase().replace(' ', '_')); + } catch(IllegalArgumentException e) { + throw newCommandException(sender, new TranslatableComponent("command.error.invalidEnum", text)); + } + } + } + public static String getDisplayName(CommandSender target) { return getDisplayName(target, null); } diff --git a/Commons/core/src/main/i18n/templates/commons/Commons.properties b/Commons/core/src/main/i18n/templates/commons/Commons.properties index 1eb5756..1defa30 100644 --- a/Commons/core/src/main/i18n/templates/commons/Commons.properties +++ b/Commons/core/src/main/i18n/templates/commons/Commons.properties @@ -10,6 +10,7 @@ command.admin.cancelRestart.noActionTaken = No active or queued restart countdow command.error.notEnoughArguments = Not enough arguments command.error.unexpectedArgument = Unexpected argument '{0}' command.error.invalidTimePeriod = Invalid time period '{0}' +command.error.invalidEnum = Invalid enum option '{0}' command.error.invalidNumber = Invalid number '{0}' command.error.invalidPage = There is no page {0}. Pages run from 1 to {1}. command.error.emptyResult = Empty result diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index e509e6a..6025635 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -248,5 +248,26 @@ item.locked = This item cannot be removed from its slot stats.hotbar = {0} kills ({1} streak) {2} deaths {3} K/D + announce.online = Announced server as online announce.offline = Announced server as offline + +blitz.countdown = Blitz mode will activate in {0} +blitz.activated = Blitz mode + +blitz.active = Blitz mode is already enabled +blitz.queued = Blitz mode is already queued to activate + +lives.change.gained.singular = You gained {0} more life +lives.change.gained.plural = You gained {0} more lives +lives.change.lost.singular = You lost {0} life +lives.change.lost.plural = You lost {0} lives + +lives.remaining.individual.singular = You have {0} life left +lives.remaining.individual.plural = You have {0} lives left +lives.remaining.team.singular = Your team has {0} life left +lives.remaining.team.plural = Your team has {0} lives left + +lives.status.eliminated = eliminated +lives.status.alive = {0} alive +lives.status.lives = {0} lives diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties index e0a6ba5..9174a5a 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMUI.properties @@ -234,12 +234,6 @@ match.score.scorebox.individual = {0} scored {1} points.singularCompound = {0} point points.pluralCompound = {0} points -# {0} = singular / plural substitution -match.blitz.livesRemaining.message = You have {0} remaining. -match.blitz.livesRemaining.singularLives = 1 life -# {0} = number of lives -match.blitz.livesRemaining.pluralLives = {0} lives - # {0} = time left in match match.timeRemaining = Time Remaining: {0} diff --git a/PGM/src/main/java/tc/oc/pgm/MapModulesManifest.java b/PGM/src/main/java/tc/oc/pgm/MapModulesManifest.java index a01aa27..01b1d9b 100644 --- a/PGM/src/main/java/tc/oc/pgm/MapModulesManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/MapModulesManifest.java @@ -1,7 +1,6 @@ package tc.oc.pgm; import tc.oc.commons.core.inject.HybridManifest; -import tc.oc.pgm.blitz.BlitzModule; import tc.oc.pgm.blockdrops.BlockDropsModule; import tc.oc.pgm.crafting.CraftingModule; import tc.oc.pgm.eventrules.EventRuleModule; @@ -13,6 +12,7 @@ import tc.oc.pgm.goals.GoalModule; import tc.oc.pgm.hunger.HungerModule; import tc.oc.pgm.itemmeta.ItemModifyModule; import tc.oc.pgm.killreward.KillRewardModule; +import tc.oc.pgm.blitz.BlitzModule; import tc.oc.pgm.map.MapModuleFactory; import tc.oc.pgm.map.StaticMethodMapModuleFactory; import tc.oc.pgm.modules.DiscardPotionBottlesModule; @@ -55,6 +55,7 @@ public class MapModulesManifest extends HybridManifest { install(new ProjectileModule.Factory()); install(new SpawnModule.Factory()); install(new TimeLimitModule.Factory()); + install(new BlitzModule.Factory()); // MapModules with static parse methods install(new StaticMethodMapModuleFactory(){}); @@ -68,7 +69,6 @@ public class MapModulesManifest extends HybridManifest { install(new StaticMethodMapModuleFactory(){}); install(new StaticMethodMapModuleFactory(){}); install(new StaticMethodMapModuleFactory(){}); - install(new StaticMethodMapModuleFactory(){}); install(new StaticMethodMapModuleFactory(){}); install(new StaticMethodMapModuleFactory(){}); install(new StaticMethodMapModuleFactory(){}); diff --git a/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java b/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java index ab00d3e..673a31a 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java @@ -12,6 +12,7 @@ import tc.oc.pgm.flag.FlagManifest; import tc.oc.pgm.itemkeep.ItemKeepManifest; import tc.oc.pgm.kits.KitManifest; import tc.oc.pgm.lane.LaneManifest; +import tc.oc.pgm.blitz.BlitzManifest; import tc.oc.pgm.loot.LootManifest; import tc.oc.pgm.modes.ObjectiveModeManifest; import tc.oc.pgm.physics.PlayerPhysicsManifest; @@ -62,5 +63,6 @@ public class PGMModulesManifest extends HybridManifest { install(new StatsManifest()); install(new RaindropManifest()); install(new ObjectiveModeManifest()); + install(new BlitzManifest()); } } diff --git a/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java b/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java index 5254722..d1f84ea 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java +++ b/PGM/src/main/java/tc/oc/pgm/api/MatchDocument.java @@ -19,9 +19,9 @@ import tc.oc.api.docs.virtual.Model; import tc.oc.api.docs.virtual.ServerDoc; import tc.oc.commons.core.stream.Collectors; import tc.oc.commons.core.util.Streams; -import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.goals.GoalMatchModule; import tc.oc.pgm.join.JoinMatchModule; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.Competitor; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchState; @@ -36,10 +36,10 @@ public class MatchDocument extends AbstractModel implements MatchDoc { private final VictoryMatchModule victory; private final Optional mutations; private final Optional goals; - private final Optional blitz; + private final BlitzMatchModule blitz; private final Optional join; - @Inject MatchDocument(ServerDoc.Identity localServer, MapDoc map, Match match, VictoryMatchModule victory, Optional mutations, Optional goals, Optional blitz, Optional join) { + @Inject MatchDocument(ServerDoc.Identity localServer, MapDoc map, Match match, VictoryMatchModule victory, Optional mutations, Optional goals, BlitzMatchModule blitz, Optional join) { this.match = match; this.localServer = localServer; this.map = map; @@ -87,7 +87,7 @@ public class MatchDocument extends AbstractModel implements MatchDoc { @Override public boolean join_mid_match() { - return !blitz.isPresent() && join.isPresent() && join.get().canJoinMid(); + return !blitz.activated() && join.isPresent() && join.get().canJoinMid(); } @Override public MapDoc map() { diff --git a/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java b/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java index 03806c0..ea84d87 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java @@ -18,6 +18,7 @@ import tc.oc.pgm.events.SetNextMapEvent; import tc.oc.pgm.ffa.events.MatchResizeEvent; import tc.oc.pgm.goals.events.GoalCompleteEvent; import tc.oc.pgm.goals.events.GoalTouchEvent; +import tc.oc.pgm.blitz.BlitzEvent; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchManager; import tc.oc.pgm.match.MatchModule; @@ -62,19 +63,25 @@ public class MatchPublishingMatchModule extends MatchModule implements Listener private final MinecraftService minecraftService; private final UpdateService matchService; private final MatchDoc matchDocument; + private final BlitzMatchModule blitz; private int initialParticipants; // Number of participants at match start (for blitz) - @Inject MatchPublishingMatchModule(Match match, MatchManager mm, MinecraftService minecraftService, UpdateService matchService, MatchDoc matchDocument) { + @Inject MatchPublishingMatchModule(Match match, MatchManager mm, MinecraftService minecraftService, UpdateService matchService, MatchDoc matchDocument, BlitzMatchModule blitz) { super(match); this.mm = mm; this.minecraftService = minecraftService; this.matchService = matchService; this.matchDocument = matchDocument; + this.blitz = blitz; } public boolean isBlitz() { - return match.hasMatchModule(BlitzMatchModule.class); + return blitz.activated(); + } + + private void countPlayers() { + this.initialParticipants = getMatch().getParticipatingPlayers().size(); } private void update() { @@ -90,7 +97,7 @@ public class MatchPublishingMatchModule extends MatchModule implements Listener @Override public void enable() { super.enable(); - this.initialParticipants = getMatch().getParticipatingPlayers().size(); + countPlayers(); update(); } @@ -111,7 +118,7 @@ public class MatchPublishingMatchModule extends MatchModule implements Listener @EventHandler(priority = EventPriority.MONITOR) public void onPartyChange(final PlayerPartyChangeEvent event) { if(!event.getMatch().hasStarted()) { - this.initialParticipants = event.getMatch().getParticipatingPlayers().size(); + countPlayers(); } update(); } @@ -122,4 +129,5 @@ public class MatchPublishingMatchModule extends MatchModule implements Listener @EventHandler(priority = EventPriority.MONITOR) public void onTeamResize(TeamResizeEvent event) { update(); } @EventHandler(priority = EventPriority.MONITOR) public void onGoalComplete(GoalCompleteEvent event) { update(); } @EventHandler(priority = EventPriority.MONITOR) public void onGoalTouch(GoalTouchEvent event) { update(); } + @EventHandler(priority = EventPriority.MONITOR) public void onBlitzEnable(BlitzEvent event) { countPlayers(); update(); } } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzConfig.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzConfig.java deleted file mode 100644 index ecbe531..0000000 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package tc.oc.pgm.blitz; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Represents information needed to run the Blitz game type. - */ -public class BlitzConfig { - public BlitzConfig(int lives, boolean broadcastLives) { - checkArgument(lives > 0, "lives must be greater than zero"); - - this.lives = lives; - this.broadcastLives = broadcastLives; - } - - /** - * Number of lives a player has during the match. - * - * @return Number of lives - */ - public int getNumLives() { - return this.lives; - } - - public boolean getBroadcastLives() { - return this.broadcastLives; - } - - final int lives; - final boolean broadcastLives; -} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzEvent.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzEvent.java new file mode 100644 index 0000000..874a2d7 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzEvent.java @@ -0,0 +1,34 @@ +package tc.oc.pgm.blitz; + +import org.bukkit.event.HandlerList; +import tc.oc.pgm.events.MatchEvent; +import tc.oc.pgm.match.Match; + +/** + * Called when {@link BlitzMatchModule} is enabled or disabled, even during a match. + */ +public class BlitzEvent extends MatchEvent { + + private final BlitzMatchModule blitz; + + public BlitzEvent(Match match, BlitzMatchModule blitz) { + super(match); + this.blitz = blitz; + } + + public BlitzMatchModule blitz() { + return blitz; + } + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzManifest.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzManifest.java new file mode 100644 index 0000000..cba923c --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzManifest.java @@ -0,0 +1,17 @@ +package tc.oc.pgm.blitz; + +import tc.oc.commons.core.inject.HybridManifest; +import tc.oc.pgm.map.inject.MapBinders; +import tc.oc.pgm.match.inject.MatchBinders; +import tc.oc.pgm.match.inject.MatchModuleFixtureManifest; + +public class BlitzManifest extends HybridManifest implements MapBinders, MatchBinders { + + @Override + protected void configure() { + bindRootElementParser(BlitzProperties.class).to(BlitzParser.class); + bind(BlitzMatchModule.class).to(BlitzMatchModuleImpl.class); + install(new MatchModuleFixtureManifest(){}); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java index 32a461f..81805dc 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java @@ -1,210 +1,93 @@ package tc.oc.pgm.blitz; -import java.util.Set; -import java.util.UUID; -import javax.annotation.Nullable; - -import com.google.api.client.util.Sets; -import com.google.common.collect.ImmutableSet; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.TranslatableComponent; -import org.bukkit.Effect; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.util.Vector; -import tc.oc.commons.core.chat.Component; -import tc.oc.commons.core.chat.Components; -import tc.oc.pgm.events.ListenerScope; -import tc.oc.pgm.events.MatchPlayerDeathEvent; -import tc.oc.pgm.events.PartyAddEvent; -import tc.oc.pgm.events.PlayerLeavePartyEvent; -import tc.oc.pgm.join.JoinDenied; -import tc.oc.pgm.join.JoinHandler; -import tc.oc.pgm.join.JoinMatchModule; -import tc.oc.pgm.join.JoinMethod; -import tc.oc.pgm.join.JoinRequest; -import tc.oc.pgm.join.JoinResult; -import tc.oc.pgm.listeners.MatchAnnouncer; import tc.oc.pgm.match.Competitor; -import tc.oc.pgm.match.Match; -import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.mutation.Mutation; -import tc.oc.pgm.mutation.MutationMatchModule; -import tc.oc.pgm.spawns.events.ParticipantReleaseEvent; -import tc.oc.pgm.victory.AbstractVictoryCondition; -import tc.oc.pgm.victory.VictoryCondition; -import tc.oc.pgm.victory.VictoryMatchModule; -@ListenerScope(MatchScope.LOADED) -public class BlitzMatchModule extends MatchModule implements Listener, JoinHandler { +import javax.annotation.Nullable; +import java.util.Optional; - final BlitzConfig config; - public final LifeManager lifeManager; - private final Set eliminatedPlayers = Sets.newHashSet(); - private int maxCompetitors; // Maximum number of non-empty Competitors that have been in the match at once +public interface BlitzMatchModule { - public class BlitzVictoryCondition extends AbstractVictoryCondition { - public BlitzVictoryCondition() { - super(Priority.BLITZ, new BlitzMatchResult()); - } + /** + * Get the properties for the blitz module. + * + * It may change during a match from {@link #activate(BlitzProperties)}. + */ + BlitzProperties properties(); - @Override public boolean isCompleted() { - // At least one competitor must be eliminated before the match can end. - // This allows maps to be tested with one or zero competitors present. - final int count = remainingCompetitors(); - return count <= 1 && count < maxCompetitors; - } + /** + * Is the blitz module *really* activated? + * + * Since the module is always loaded on the chance it should + * be activated during a match, it is only actively enforcing + * its rules when this returns true. + */ + boolean activated(); + + /** + * Activate the blitz module with a new set of properties. + * + * If the properties are null, it will default to its current + * {@link #properties()}. + */ + void activate(@Nullable BlitzProperties properties); + + default void activate() { + activate(null); } - final VictoryCondition victoryCondition = new BlitzVictoryCondition(); + /** + * Deactivate the blitz module by clearing all of its data. + * + * The module can be activated and deactivated as many times + * as you need. + */ + void deactivate(); - public BlitzMatchModule(Match match, BlitzConfig config) { - super(match); - this.config = match.module(MutationMatchModule.class).get().enabled(Mutation.BLITZ) ? new BlitzConfig(1, true) : config; - this.lifeManager = new LifeManager(this.config.getNumLives()); - } + /** + * Increment the number of lives for a player if + * {@link #eliminated(MatchPlayer)} is false. + * + * If notify is true, the player will get a message + * explaining how much lives they gained or lost. + * + * If immediate is true and the player's lives are empty, + * {@link #eliminate(MatchPlayer)} will be called and the + * player will be out of the game. + * + * @return Whether the player is now {@link #eliminated(MatchPlayer)}. + */ + boolean increment(MatchPlayer player, int lives, boolean notify, boolean immediate); - @Override - public boolean shouldLoad() { - return super.shouldLoad() && config.lives != Integer.MAX_VALUE; - } + /** + * Is the player eliminated from the match? + * + * This value can change back to false if the player were + * forced onto another team after being eliminated. + */ + boolean eliminated(MatchPlayer player); - @Override - public void load() { - super.load(); - match.needMatchModule(JoinMatchModule.class).registerHandler(this); - match.needMatchModule(VictoryMatchModule.class).setVictoryCondition(victoryCondition); - } + /** + * Eliminate the player from this match by moving + * them to the default party and preventing them from respawning. + */ + void eliminate(MatchPlayer player); - @Override - public void enable() { - super.enable(); - updateMaxCompetitors(); - } + /** + * Try to get the lives for this player. + */ + Optional lives(MatchPlayer player); - private int remainingCompetitors() { - return (int) match.getCompetitors() - .stream() - .filter(c -> !c.getPlayers().isEmpty()) - .count(); - } + /** + * Get the amount of lives a player has left. + * + * @throws IllegalStateException if {@link #lives(MatchPlayer)} is not present. + */ + int livesCount(MatchPlayer player); - @EventHandler - public void onPartyAdd(PartyAddEvent event) { - if(event.getParty() instanceof Competitor) { - updateMaxCompetitors(); - } - } - - private void updateMaxCompetitors() { - maxCompetitors = Math.max(maxCompetitors, remainingCompetitors()); - } - - public BlitzConfig getConfig() { - return this.config; - } - - /** Whether or not the player participated in the match and was eliminated. */ - public boolean isPlayerEliminated(UUID player) { - return this.eliminatedPlayers.contains(player); - } - - public int getRemainingPlayers(Competitor competitor) { - // TODO: this becomes a bit more complex when eliminated players are not forced to observers - return competitor.getPlayers().size(); - } - - @Override - public @Nullable JoinResult queryJoin(MatchPlayer joining, JoinRequest request) { - if(getMatch().hasStarted() && request.method() != JoinMethod.FORCE) { - // This message should NOT look like an error, because remotely joining players will see it often. - // It also should not say "Blitz" because not all maps that use this module want to be labelled "Blitz". - return JoinDenied.friendly("command.gameplay.join.matchStarted"); - } - return null; - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void handleDeath(final MatchPlayerDeathEvent event) { - MatchPlayer victim = event.getVictim(); - if(victim.getParty() instanceof Competitor) { - int lives = this.lifeManager.addLives(event.getVictim().getPlayerId(), -1); - if(lives <= 0) { - this.handleElimination(victim); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void handleLeave(final PlayerLeavePartyEvent event) { - if(!match.isRunning()) return; - int lives = this.lifeManager.getLives(event.getPlayer().getPlayerId()); - if (event.getOldParty() instanceof Competitor && lives > 0) { - // Player switching teams, check if match needs to end - if (event.getNewParty() instanceof Competitor) checkEnd(); - // Player is going to obs, eliminate it - else handleElimination(event.getPlayer()); - } - } - - @EventHandler - public void handleSpawn(final ParticipantReleaseEvent event) { - if(this.config.broadcastLives) { - int lives = this.lifeManager.getLives(event.getPlayer().getPlayerId()); - event.getPlayer().showTitle( - // Fake the "Go!" title at match start - event.wasFrozen() ? MatchAnnouncer.GO : Components.blank(), - new Component( - new TranslatableComponent( - "match.blitz.livesRemaining.message", - new Component( - new TranslatableComponent( - lives == 1 ? "match.blitz.livesRemaining.singularLives" - : "match.blitz.livesRemaining.pluralLives", - Integer.toString(lives) - ), - ChatColor.AQUA - ) - ), - ChatColor.RED - ), - 0, 60, 20 - ); - } - } - - private void handleElimination(final MatchPlayer player) { - if (!eliminatedPlayers.add(player.getBukkit().getUniqueId())) return; - - World world = player.getMatch().getWorld(); - Location death = player.getBukkit().getLocation(); - - double radius = 0.1; - int n = 8; - for(int i = 0; i < 6; i++) { - double angle = 2 * Math.PI * i / n; - Location base = death.clone().add(new Vector(radius * Math.cos(angle), 0, radius * Math.sin(angle))); - for(int j = 0; j <= 8; j++) { - world.playEffect(base, Effect.SMOKE, j); - } - } - checkEnd(); - } - - private void checkEnd() { - // Process eliminations within the same tick simultaneously, so that ties are properly detected - getMatch().getScheduler(MatchScope.RUNNING).debounceTask(() -> { - ImmutableSet.copyOf(getMatch().getParticipatingPlayers()) - .stream() - .filter(participating -> eliminatedPlayers.contains(participating.getBukkit().getUniqueId())) - .forEach(participating -> match.setPlayerParty(participating, match.getDefaultParty())); - match.needMatchModule(VictoryMatchModule.class).invalidateAndCheckEnd(); - }); - } + /** + * Try to get the team lives for this competitor. + */ + Optional lives(Competitor competitor); } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java new file mode 100644 index 0000000..cd7bad7 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java @@ -0,0 +1,283 @@ +package tc.oc.pgm.blitz; + +import com.google.common.collect.ImmutableSet; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.event.EventException; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import tc.oc.api.docs.PlayerId; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; +import tc.oc.pgm.events.ListenerScope; +import tc.oc.pgm.events.MatchPlayerDeathEvent; +import tc.oc.pgm.events.PartyAddEvent; +import tc.oc.pgm.events.PlayerChangePartyEvent; +import tc.oc.pgm.join.JoinDenied; +import tc.oc.pgm.join.JoinHandler; +import tc.oc.pgm.join.JoinMatchModule; +import tc.oc.pgm.join.JoinMethod; +import tc.oc.pgm.join.JoinRequest; +import tc.oc.pgm.join.JoinResult; +import tc.oc.pgm.listeners.MatchAnnouncer; +import tc.oc.pgm.match.Competitor; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchModule; +import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.spawns.events.ParticipantReleaseEvent; +import tc.oc.pgm.teams.TeamMatchModule; +import tc.oc.pgm.victory.VictoryMatchModule; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +@ListenerScope(MatchScope.LOADED) +public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModule, Listener, JoinHandler { + + private final Match match; + private final World world; + private final JoinMatchModule join; + private final VictoryMatchModule victory; + private final Optional teams; + private BlitzProperties properties; + + private final Set lives = new HashSet<>(); + private final Set eliminated = new HashSet<>(); + private boolean activated = false; + private int competitors = 0; + + @Inject BlitzMatchModuleImpl(Match match, World world, JoinMatchModule join, VictoryMatchModule victory, Optional teams, BlitzProperties properties) { + this.match = match; + this.world = world; + this.join = join; + this.victory = victory; + this.teams = teams; + this.properties = properties; + } + + private void preload() { + if(!properties().empty()) { + activate(); + } + } + + protected int competitors() { + return competitors; + } + + protected int remainingCompetitors() { + return (int) match.getCompetitors() + .stream() + .filter(c -> !c.getPlayers().isEmpty()) + .count(); + } + + private void updateCompetitors() { + competitors = Math.max(competitors(), remainingCompetitors()); + } + + private void setup(MatchPlayer player, boolean force) { + if(force) { + eliminated.remove(player.getPlayerId()); + lives.removeIf(life -> life.owner(player.getPlayerId())); + } + switch(properties().type) { + case INDIVIDUAL: + properties().individuals.forEach((filter, count) -> { + if(filter.allows(player)) { + lives.add(new LivesIndividual(player, count)); + } + }); break; + case TEAM: + properties().teams.forEach((teamFactory, count) -> { + if(teams.get().team(teamFactory).equals(player.getCompetitor())) { + lives.add(new LivesTeam(player.getCompetitor(), count)); + } + }); break; + } + } + + private void showLives(MatchPlayer player, boolean release, boolean activate) { + final Optional lives = lives(player); + if(activated() && lives.isPresent()) { + player.showTitle( + release ? MatchAnnouncer.GO + : activate ? new Component(new TranslatableComponent("blitz.activated"), ChatColor.GREEN) + : Components.blank(), + lives.get().remaining(), + 0, 60, 20 + ); + } + } + + @Override + public boolean activated() { + return activated; + } + + @Override + public void activate(@Nullable BlitzProperties newProperties) { + if(!activated) { + activated = true; + if(newProperties != null) { + properties = newProperties; + } + load(); + if(match.hasStarted()) { + enable(); + } + } + } + + @Override + public void deactivate() { + activated = false; + lives.clear(); + eliminated.clear(); + } + + @Override + public void load() { + if(activated()) { + join.registerHandler(this); + victory.setVictoryCondition(new BlitzVictoryCondition(this)); + } else { + preload(); + } + } + + @Override + public void enable() { + if(activated()) { + updateCompetitors(); + match.participants().forEach(player -> { + setup(player, false); + if(match.hasStarted()) { + showLives(player, false, true); + } + }); + match.callEvent(new BlitzEvent(match, this)); + } + } + + @Override + public BlitzProperties properties() { + return properties; + } + + @Override + public boolean increment(MatchPlayer player, int lives, boolean notify, boolean immediate) { + if(!eliminated(player)) { + return lives(player).map(life -> { + life.add(player.getPlayerId(), lives); + if(notify) { + player.showTitle(Components.blank(), life.change(lives), 0, 40, 10); + } + if(life.empty() && immediate) { + eliminate(player); + return true; + } + return false; + }).orElse(false); + } + return true; + } + + @Override + public int livesCount(MatchPlayer player) { + return lives(player).map(Lives::current) + .orElseThrow(() -> new IllegalStateException(player + " has no lives present to count")); + } + + @Override + public Optional lives(MatchPlayer player) { + return lives.stream() + .filter(lives -> lives.applicableTo(player.getPlayerId())) + .findFirst(); + } + + @Override + public Optional lives(Competitor competitor) { + return lives.stream() + .filter(lives -> lives.type().equals(Lives.Type.TEAM) && lives.competitor().equals(competitor)) + .findFirst(); + } + + @Override + public boolean eliminated(MatchPlayer player) { + return eliminated.contains(player.getPlayerId()); + } + + @Override + public void eliminate(MatchPlayer player) { + if(activated() && !eliminated(player)) { + eliminated.add(player.getPlayerId()); + // Process eliminations within the same tick simultaneously, so that ties are properly detected + match.getScheduler(MatchScope.RUNNING).debounceTask(() -> { + ImmutableSet.copyOf(getMatch().getParticipatingPlayers()) + .stream() + .filter(this::eliminated) + .forEach(participating -> { + match.setPlayerParty(participating, match.getDefaultParty()); + world.spawnParticle(Particle.SMOKE_LARGE, player.getLocation(), 5); + }); + victory.invalidateAndCheckEnd(); + }); + } + } + + @Override + public JoinResult queryJoin(MatchPlayer joining, JoinRequest request) { + if(activated() && + match.hasStarted() && + !EnumSet.of(JoinMethod.FORCE, JoinMethod.REMOTE).contains(request.method())) { + // This message should NOT look like an error, because remotely joining players will see it often. + // It also should not say "Blitz" because not all maps that use this module want to be labelled "Blitz". + return JoinDenied.friendly("command.gameplay.join.matchStarted"); + } + return null; + } + + @EventHandler + public void onPartyAdd(PartyAddEvent event) { + if(event.getParty() instanceof Competitor) { + updateCompetitors(); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPartyChange(PlayerChangePartyEvent event) throws EventException { + final MatchPlayer player = event.getPlayer(); + if(event.getNewParty() == null) { + if(event.getOldParty() instanceof Competitor && match.hasStarted() && !increment(player, -1, false, true)) { + eliminate(player); + } + } else if(event.getNewParty() instanceof Competitor) { + event.yield(); + setup(player, true); + updateCompetitors(); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onDeath(MatchPlayerDeathEvent event) { + final MatchPlayer player = event.getVictim(); + if(player.competitor().isPresent()) { + increment(player, -1, false, true); + } + } + + @EventHandler + public void onRelease(ParticipantReleaseEvent event) { + showLives(event.getPlayer(), event.wasFrozen(), false); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchResult.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchResult.java index 4550ec0..19e82f7 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchResult.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchResult.java @@ -6,6 +6,7 @@ import tc.oc.pgm.match.Competitor; import tc.oc.pgm.victory.MatchResult; public class BlitzMatchResult implements MatchResult { + @Override public int compare(Competitor a, Competitor b) { return Integer.compare(b.getPlayers().size(), a.getPlayers().size()); @@ -15,4 +16,5 @@ public class BlitzMatchResult implements MatchResult { public BaseComponent describeResult() { return new Component("most survivors"); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzModule.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzModule.java index 5b67b75..3cf8b6d 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzModule.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzModule.java @@ -1,85 +1,60 @@ package tc.oc.pgm.blitz; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.logging.Logger; -import com.google.common.collect.Range; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import org.jdom2.Document; -import org.jdom2.Element; import tc.oc.api.docs.virtual.MapDoc; -import tc.oc.pgm.ffa.FreeForAllModule; import tc.oc.pgm.map.MapModule; import tc.oc.pgm.map.MapModuleContext; -import tc.oc.pgm.match.Match; -import tc.oc.pgm.match.MatchModuleFactory; -import tc.oc.pgm.module.ModuleDescription; -import tc.oc.pgm.mutation.MutationMapModule; -import tc.oc.pgm.mutation.MutationMatchModule; -import tc.oc.pgm.teams.TeamModule; -import tc.oc.pgm.utils.XMLUtils; +import tc.oc.pgm.map.MapModuleFactory; import tc.oc.pgm.xml.InvalidXMLException; -import tc.oc.pgm.xml.Node; -import static com.google.common.base.Preconditions.checkNotNull; +import javax.inject.Inject; +import javax.inject.Provider; +public class BlitzModule implements MapModule { -@ModuleDescription(name = "Blitz", follows = MutationMapModule.class) -public class BlitzModule implements MapModule, MatchModuleFactory { - final BlitzConfig config; + private final BlitzProperties properties; - public BlitzModule(BlitzConfig config) { - this.config = checkNotNull(config); + public BlitzModule(BlitzProperties properties) { + this.properties = properties; + } + + public boolean active() { + return !properties.empty(); } @Override public Set getGamemodes(MapModuleContext context) { - return isEnabled() ? Collections.singleton(MapDoc.Gamemode.blitz) : Collections.emptySet(); + return active() ? Collections.singleton(MapDoc.Gamemode.blitz) : Collections.emptySet(); } @Override public BaseComponent getGameName(MapModuleContext context) { - if(!isEnabled()) return null; - if (context.hasModule(TeamModule.class)) { + if(!active()) { + return null; + } else if(!properties.multipleLives()) { return new TranslatableComponent("match.scoreboard.playersRemaining.title"); - } else if (context.hasModule(FreeForAllModule.class) && config.getNumLives() > 1) { + } else if(properties.teams.isEmpty()) { return new TranslatableComponent("match.scoreboard.livesRemaining.title"); } else { return new TranslatableComponent("match.scoreboard.blitz.title"); } } - @Override - public BlitzMatchModule createMatchModule(Match match) { - return new BlitzMatchModule(match, this.config); - } + public static class Factory extends MapModuleFactory { - /** - * In order to support {@link MutationMatchModule}, this module - * will always create a {@link BlitzMatchModule}. However, if the lives are set to - * {@link Integer#MAX_VALUE}, then it will fail to load on {@link BlitzMatchModule#shouldLoad()}. - */ - public boolean isEnabled() { - return config.lives != Integer.MAX_VALUE; - } + @Inject Provider propertiesProvider; - // --------------------- - // ---- XML Parsing ---- - // --------------------- - - public static BlitzModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException { - List blitzElements = doc.getRootElement().getChildren("blitz"); - BlitzConfig config = new BlitzConfig(Integer.MAX_VALUE, false); - - for(Element blitzEl : blitzElements) { - boolean broadcastLives = XMLUtils.parseBoolean(blitzEl.getChild("broadcastLives"), true); - int lives = XMLUtils.parseNumber(Node.fromChildOrAttr(blitzEl, "lives"), Integer.class, Range.atLeast(1), 1); - config = new BlitzConfig(lives, broadcastLives); + @Override + public BlitzModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException { + return new BlitzModule(propertiesProvider.get()); } - return new BlitzModule(config); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzParser.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzParser.java new file mode 100644 index 0000000..81fa586 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzParser.java @@ -0,0 +1,72 @@ +package tc.oc.pgm.blitz; + +import com.google.common.collect.Range; +import com.google.inject.Provider; +import org.jdom2.Element; +import tc.oc.pgm.filters.Filter; +import tc.oc.pgm.filters.matcher.StaticFilter; +import tc.oc.pgm.filters.parser.FilterParser; +import tc.oc.pgm.teams.TeamFactory; +import tc.oc.pgm.utils.XMLUtils; +import tc.oc.pgm.xml.InvalidXMLException; +import tc.oc.pgm.xml.Node; +import tc.oc.pgm.xml.parser.ElementParser; + +import javax.inject.Inject; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static tc.oc.pgm.blitz.BlitzProperties.*; + +public class BlitzParser implements ElementParser { + + private final FilterParser filters; + private final Provider> factories; + + @Inject private BlitzParser(FilterParser filters, Provider> factories) { + this.filters = filters; + this.factories = factories; + } + + @Override + public BlitzProperties parseElement(Element element) throws InvalidXMLException { + boolean broadcast = true; + int global = -1; + + Map individuals = new HashMap<>(); + Map teams = factories.get().stream() + .filter(team -> team.getLives().isPresent()) + .collect(Collectors.toMap(Function.identity(), team -> team.getLives().get())); + + for(Element el : XMLUtils.getChildren(element, "blitz")) { + broadcast = XMLUtils.parseBoolean(Node.fromChildOrAttr(el, "broadcast", "broadcastLives"), broadcast); + global = XMLUtils.parseNumber(Node.fromChildOrAttr(el, "lives"), Integer.class, Range.atLeast(1), global); + if(global != -1) { + individuals.put(StaticFilter.ALLOW, global); + } else { + for(Element e : XMLUtils.getChildren(el, "rule")) { + individuals.put( + filters.parse(Node.fromChildOrAttr(e, "filter")), + XMLUtils.parseNumber(Node.fromChildOrAttr(e, "lives"), Integer.class, Range.atLeast(1), 1) + ); + } + } + } + + if(!individuals.isEmpty() && teams.isEmpty()) { + return individuals(individuals, broadcast); + } else if(individuals.isEmpty() && !teams.isEmpty()) { + return teams(teams, broadcast); + } else if(!individuals.isEmpty() && !teams.isEmpty()) { + throw new InvalidXMLException("Cannot define both team respawns and blitz"); + } else { + return none(); + } + + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzProperties.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzProperties.java new file mode 100644 index 0000000..d818249 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzProperties.java @@ -0,0 +1,64 @@ +package tc.oc.pgm.blitz; + +import tc.oc.commons.core.IterableUtils; +import tc.oc.commons.core.util.MapUtils; +import tc.oc.pgm.filters.Filter; +import tc.oc.pgm.filters.matcher.StaticFilter; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.teams.Team; +import tc.oc.pgm.teams.TeamFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class BlitzProperties { + + public final Map teams; + public final Map individuals; + public final Lives.Type type; + public final boolean broadcast; + + private final boolean multi; + private final boolean empty; + + public BlitzProperties(Map teams, Map individuals, Lives.Type type, boolean broadcast) { + this.teams = teams; + this.individuals = individuals; + this.type = type; + this.broadcast = broadcast; + this.multi = IterableUtils.any(IterableUtils.concat(teams.values(), individuals.values()), i -> i != 1); + this.empty = teams.isEmpty() && individuals.isEmpty(); + } + + public static BlitzProperties none() { + return new BlitzProperties(new HashMap<>(), new HashMap<>(), Lives.Type.INDIVIDUAL, false); + } + + public static BlitzProperties individuals(Map individuals, boolean broadcast) { + return new BlitzProperties(new HashMap<>(), individuals, Lives.Type.INDIVIDUAL, broadcast); + } + + public static BlitzProperties teams(Map teams, boolean broadcast) { + return new BlitzProperties(teams, new HashMap<>(), Lives.Type.TEAM, broadcast); + } + + public static BlitzProperties create(Match match, int lives, Lives.Type type) { + return new BlitzProperties( + match.competitors().filter(c -> c instanceof Team).map(c -> ((Team) c).getDefinition()).collect(Collectors.toMap(Function.identity(), c -> lives)), + MapUtils.merge(new HashMap<>(), StaticFilter.ALLOW, lives), + type, + true + ); + } + + public boolean multipleLives() { + return multi; + } + + public boolean empty() { + return empty; + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzVictoryCondition.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzVictoryCondition.java new file mode 100644 index 0000000..1e4a6e5 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzVictoryCondition.java @@ -0,0 +1,22 @@ +package tc.oc.pgm.blitz; + +import tc.oc.pgm.victory.AbstractVictoryCondition; + +public class BlitzVictoryCondition extends AbstractVictoryCondition { + + private final BlitzMatchModuleImpl blitz; + + protected BlitzVictoryCondition(BlitzMatchModuleImpl blitz) { + super(Priority.BLITZ, new BlitzMatchResult()); + this.blitz = blitz; + } + + @Override + public boolean isCompleted() { + // At least one competitor must be eliminated before the match can end. + // This allows maps to be tested with one or zero competitors present. + final int count = blitz.remainingCompetitors(); + return blitz.activated() && count <= 1 && count < blitz.competitors(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LifeManager.java b/PGM/src/main/java/tc/oc/pgm/blitz/LifeManager.java deleted file mode 100644 index 2c1b005..0000000 --- a/PGM/src/main/java/tc/oc/pgm/blitz/LifeManager.java +++ /dev/null @@ -1,45 +0,0 @@ -package tc.oc.pgm.blitz; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Map; - -import com.google.common.collect.Maps; -import tc.oc.api.docs.PlayerId; - -public class LifeManager { - - final int lives; - final Map livesLeft = Maps.newHashMap(); - - public LifeManager(int lives) { - checkArgument(lives > 0, "lives must be greater than zero"); - - this.lives = lives; - } - - public int getLives() { - return this.lives; - } - - public int getLives(PlayerId player) { - checkNotNull(player, "player id"); - - Integer livesLeft = this.livesLeft.get(player); - if(livesLeft != null) { - return livesLeft; - } else { - return this.lives; - } - } - - public int addLives(PlayerId player, int dlives) { - checkNotNull(player, "player id"); - - int lives = Math.max(0, this.getLives(player) + dlives); - this.livesLeft.put(player, lives); - - return lives; - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/Lives.java b/PGM/src/main/java/tc/oc/pgm/blitz/Lives.java new file mode 100644 index 0000000..0de7562 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/Lives.java @@ -0,0 +1,73 @@ +package tc.oc.pgm.blitz; + +import net.md_5.bungee.api.chat.BaseComponent; +import tc.oc.api.docs.PlayerId; +import tc.oc.pgm.match.Competitor; + +import javax.annotation.Nullable; + +public interface Lives { + + /** + * Original amount of lives. + */ + int original(); + + /** + * Current amount of lives (may be larger than {@link #original()}, + * due to players getting addition lives via kits). + */ + int current(); + + /** + * Add more to the current lives and include the player that + * caused this change if applicable. + */ + void add(@Nullable PlayerId cause, int delta); + + /** + * Get the delta number of life changes this player has caused. + */ + int changesBy(PlayerId player); + + /** + * Are the amount of lives reduced when this player dies? + */ + boolean applicableTo(PlayerId player); + + /** + * Is this player the sole owner of these lives? + */ + boolean owner(PlayerId playerId); + + /** + * Are there no lives left? + */ + boolean empty(); + + /** + * Get the competitor relation of these lives. + */ + Competitor competitor(); + + /** + * Message sent to players notifying them how many lives they have left. + */ + BaseComponent remaining(); + + /** + * Sidebar status of how many respawns a competitor has left. + */ + BaseComponent status(); + + /** + * Message sent when a player gains or loses lives. + */ + BaseComponent change(int delta); + + /** + * Implementations of lives as an enum. + */ + Type type(); enum Type { TEAM, INDIVIDUAL } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java new file mode 100644 index 0000000..77d7154 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java @@ -0,0 +1,113 @@ +package tc.oc.pgm.blitz; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; +import tc.oc.api.docs.PlayerId; +import tc.oc.commons.core.chat.Component; +import tc.oc.pgm.match.Competitor; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public abstract class LivesBase implements Lives { + + private final Map deltas; + private final Competitor competitor; + private final int original; + private int current; + + public LivesBase(int lives, Competitor competitor) { + this.deltas = new HashMap<>(); + this.competitor = competitor; + this.original = lives; + this.current = lives; + update(); + } + + private void update() { + competitor().getMatch().callEvent(new LivesEvent(this)); + } + + @Override + public Competitor competitor() { + return competitor; + } + + @Override + public int original() { + return original; + } + + @Override + public int current() { + return current; + } + + @Override + public boolean empty() { + return current() <= 0; + } + + @Override + public void add(@Nullable PlayerId cause, int delta) { + current = Math.max(0, current() + delta); + deltas.put(cause, changesBy(cause) + delta); + update(); + } + + @Override + public int changesBy(PlayerId player) { + return deltas.getOrDefault(player, 0); + } + + @Override + public BaseComponent remaining() { + return new Component( + new TranslatableComponent( + "lives.remaining." + type().name().toLowerCase() + "." + (current() == 1 ? "singular" : "plural"), + new Component(current(), ChatColor.YELLOW) + ), + ChatColor.AQUA + ); + } + + @Override + public BaseComponent status() { + int alive = (int) competitor().players().count(); + return new Component( + Stream.of( + new Component("("), + new TranslatableComponent( + empty() ? alive == 0 ? "lives.status.eliminated" + : "lives.status.alive" + : "lives.status.lives", + new Component( + empty() ? alive : current(), + ChatColor.WHITE + ) + ), + new Component(")") + ), + ChatColor.GRAY, + ChatColor.ITALIC + ); + } + + @Override + public BaseComponent change(int delta) { + int absDelta = Math.abs(delta); + return new Component( + new TranslatableComponent( + (delta > 0 ? "lives.change.gained." + : "lives.change.lost.") + (absDelta == 1 ? "singular" + : "plural"), + new Component(absDelta, ChatColor.AQUA) + ), + ChatColor.WHITE + ); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesEvent.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesEvent.java new file mode 100644 index 0000000..bebe2c1 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesEvent.java @@ -0,0 +1,32 @@ +package tc.oc.pgm.blitz; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Called when the amount of {@link Lives} changes. + */ +public class LivesEvent extends Event { + + private final Lives lives; + + public LivesEvent(Lives lives) { + this.lives = lives; + } + + public Lives lives() { + return lives; + } + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesIndividual.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesIndividual.java new file mode 100644 index 0000000..5205401 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesIndividual.java @@ -0,0 +1,46 @@ +package tc.oc.pgm.blitz; + +import tc.oc.api.docs.PlayerId; +import tc.oc.pgm.match.MatchPlayer; + +public class LivesIndividual extends LivesBase { + + private final PlayerId player; + + public LivesIndividual(MatchPlayer player, int lives) { + super(lives, player.getCompetitor()); + this.player = player.getPlayerId(); + } + + public PlayerId player() { + return player; + } + + @Override + public Type type() { + return Type.INDIVIDUAL; + } + + @Override + public boolean applicableTo(PlayerId player) { + return player().equals(player); + } + + @Override + public boolean owner(PlayerId playerId) { + return player().equals(playerId); + } + + @Override + public int hashCode() { + return player().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && + obj instanceof LivesIndividual && + player().equals(((LivesIndividual) obj).player()); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesKit.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesKit.java new file mode 100644 index 0000000..9324f4e --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesKit.java @@ -0,0 +1,20 @@ +package tc.oc.pgm.blitz; + +import tc.oc.pgm.kits.ItemKitApplicator; +import tc.oc.pgm.kits.Kit; +import tc.oc.pgm.match.MatchPlayer; + +public class LivesKit extends Kit.Impl { + + private final int lives; + + public LivesKit(int lives) { + this.lives = lives; + } + + @Override + public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) { + player.getMatch().module(BlitzMatchModuleImpl.class).ifPresent(blitz -> blitz.increment(player, lives, true, force)); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java new file mode 100644 index 0000000..f989a87 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java @@ -0,0 +1,39 @@ +package tc.oc.pgm.blitz; + +import tc.oc.api.docs.PlayerId; +import tc.oc.pgm.match.Competitor; + +public class LivesTeam extends LivesBase { + + public LivesTeam(Competitor competitor, int lives) { + super(lives, competitor); + } + + @Override + public Type type() { + return Type.TEAM; + } + + @Override + public boolean applicableTo(PlayerId player) { + return competitor().players().anyMatch(matchPlayer -> matchPlayer.getPlayerId().equals(player)); + } + + @Override + public boolean owner(PlayerId playerId) { + return false; + } + + @Override + public int hashCode() { + return competitor().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && + obj instanceof LivesTeam && + competitor().equals(((LivesTeam) obj).competitor()); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/countdowns/CountdownContext.java b/PGM/src/main/java/tc/oc/pgm/countdowns/CountdownContext.java index 851d5b0..4f09adc 100644 --- a/PGM/src/main/java/tc/oc/pgm/countdowns/CountdownContext.java +++ b/PGM/src/main/java/tc/oc/pgm/countdowns/CountdownContext.java @@ -110,6 +110,10 @@ public abstract class CountdownContext implements Listener { return countdowns().anyMatch(test); } + public boolean anyRunning(Class countdownClass) { + return anyRunning(countdown -> countdown.getClass().equals(countdownClass)); + } + public void cancelAll() { cancelAll(false); } diff --git a/PGM/src/main/java/tc/oc/pgm/ghostsquadron/GhostSquadronModule.java b/PGM/src/main/java/tc/oc/pgm/ghostsquadron/GhostSquadronModule.java index fb7b344..75d1833 100644 --- a/PGM/src/main/java/tc/oc/pgm/ghostsquadron/GhostSquadronModule.java +++ b/PGM/src/main/java/tc/oc/pgm/ghostsquadron/GhostSquadronModule.java @@ -9,9 +9,9 @@ import net.md_5.bungee.api.chat.TranslatableComponent; import org.jdom2.Document; import org.jdom2.Element; import tc.oc.api.docs.virtual.MapDoc; -import tc.oc.pgm.blitz.BlitzModule; import tc.oc.pgm.classes.ClassMatchModule; import tc.oc.pgm.classes.ClassModule; +import tc.oc.pgm.blitz.BlitzModule; import tc.oc.pgm.map.MapModule; import tc.oc.pgm.map.MapModuleContext; import tc.oc.pgm.match.Match; diff --git a/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java b/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java index 9a6c527..ffb82f7 100644 --- a/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java @@ -38,17 +38,17 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffect; -import tc.oc.api.bukkit.users.Users; import tc.oc.commons.bukkit.util.BukkitUtils; import tc.oc.commons.core.commands.CommandBinder; import tc.oc.pgm.PGMTranslations; -import tc.oc.pgm.blitz.BlitzMatchModule; +import tc.oc.pgm.blitz.BlitzMatchModuleImpl; import tc.oc.pgm.doublejump.DoubleJumpMatchModule; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.events.ObserverInteractEvent; import tc.oc.pgm.events.PlayerBlockTransformEvent; import tc.oc.pgm.events.PlayerPartyChangeEvent; import tc.oc.pgm.kits.WalkSpeedKit; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.MatchScope; @@ -269,9 +269,9 @@ public class ViewInventoryMatchModule extends MatchModule implements Listener { MatchPlayer matchHolder = this.match.getPlayer(holder); if (matchHolder != null && matchHolder.isParticipating()) { - BlitzMatchModule module = matchHolder.getMatch().getMatchModule(BlitzMatchModule.class); - if (module != null) { - int livesLeft = module.lifeManager.getLives(Users.playerId(holder)); + BlitzMatchModule module = matchHolder.getMatch().getMatchModule(BlitzMatchModuleImpl.class); + if(module != null) { + int livesLeft = module.livesCount(matchHolder); ItemStack lives = new ItemStack(Material.EGG, livesLeft); ItemMeta lifeMeta = lives.getItemMeta(); lifeMeta.addItemFlags(ItemFlag.values()); diff --git a/PGM/src/main/java/tc/oc/pgm/kits/KitDefinitionParser.java b/PGM/src/main/java/tc/oc/pgm/kits/KitDefinitionParser.java index de4cd95..dc654ec 100644 --- a/PGM/src/main/java/tc/oc/pgm/kits/KitDefinitionParser.java +++ b/PGM/src/main/java/tc/oc/pgm/kits/KitDefinitionParser.java @@ -10,6 +10,7 @@ import org.bukkit.inventory.ItemStack; import org.jdom2.Element; import tc.oc.commons.bukkit.inventory.ArmorType; import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.pgm.blitz.LivesKit; import tc.oc.pgm.compose.CompositionParser; import tc.oc.pgm.doublejump.DoubleJumpKit; import tc.oc.pgm.features.FeatureDefinitionContext; @@ -254,4 +255,9 @@ public class KitDefinitionParser extends MagicMethodFeatureParser implement return new ForceKit(XMLUtils.parseVector(new Node(el)), parseRelativeFlags(el)); } + + @MethodParser + private Kit lives(Element el) throws InvalidXMLException { + return new LivesKit(XMLUtils.parseNumber(new Node(el), Integer.class, false, +1)); + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java b/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java index affc605..98a9bba 100644 --- a/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java @@ -37,9 +37,9 @@ import tc.oc.commons.core.commands.CommandFutureCallback; import tc.oc.commons.core.formatting.StringUtils; import tc.oc.commons.core.util.Comparables; import tc.oc.pgm.PGMTranslations; -import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.events.PlayerLeaveMatchEvent; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchExecutor; import tc.oc.pgm.match.MatchModule; @@ -98,6 +98,7 @@ public class MapRatingsMatchModule extends MatchModule implements Listener { @Inject private MapRatingsConfiguration config; @Inject private MapService mapService; @Inject private MatchExecutor matchExecutor; + @Inject private BlitzMatchModule blitz; private final Map playerRatings = new HashMap<>(); @@ -140,10 +141,8 @@ public class MapRatingsMatchModule extends MatchModule implements Listener { return PGMTranslations.t("noPermission", player); } - BlitzMatchModule blitz = player.getMatch().getMatchModule(BlitzMatchModule.class); - if(Comparables.lessThan(player.getCumulativeParticipationTime(), MIN_PARTICIPATION_TIME) && - !(blitz != null && blitz.isPlayerEliminated(player.getBukkit().getUniqueId())) && + !(blitz.eliminated(player)) && !(this.getMatch().isFinished() && player.getCumulativeParticipationPercent() > MIN_PARTICIPATION_PERCENT)) { return PGMTranslations.t("rating.lowParticipation", player); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java index 575af58..35404d9 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/Mutation.java @@ -23,6 +23,7 @@ import tc.oc.pgm.mutation.types.kit.MobsMutation; import tc.oc.pgm.mutation.types.kit.PotionMutation; import tc.oc.pgm.mutation.types.kit.ProjectileMutation; import tc.oc.pgm.mutation.types.kit.StealthMutation; +import tc.oc.pgm.mutation.types.other.BlitzMutation; import tc.oc.pgm.mutation.types.other.RageMutation; import tc.oc.pgm.mutation.types.targetable.ApocalypseMutation; import tc.oc.pgm.mutation.types.targetable.BomberMutation; @@ -32,7 +33,7 @@ import java.util.stream.Stream; public enum Mutation { - BLITZ (null), + BLITZ (BlitzMutation.class), RAGE (RageMutation.class), HARDCORE (HardcoreMutation.class), JUMP (JumpMutation.class), diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java new file mode 100644 index 0000000..7062f61 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java @@ -0,0 +1,47 @@ +package tc.oc.pgm.mutation.types.other; + +import com.google.common.collect.Range; +import org.apache.commons.lang.math.Fraction; +import tc.oc.commons.core.random.RandomUtils; +import tc.oc.pgm.blitz.BlitzMatchModuleImpl; +import tc.oc.pgm.blitz.BlitzProperties; +import tc.oc.pgm.blitz.Lives; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.mutation.types.MutationModule; +import tc.oc.pgm.teams.TeamMatchModule; + +public class BlitzMutation extends MutationModule { + + final static Range LIVES = Range.closed(1, 3); + final static Fraction TEAM_CHANCE = Fraction.ONE_QUARTER; + + public BlitzMutation(Match match) { + super(match); + } + + @Override + public void enable() { + super.enable(); + int lives = match.entropyForTick().randomInt(LIVES); + Lives.Type type; + if(match.module(TeamMatchModule.class).isPresent() && RandomUtils.nextBoolean(random, TEAM_CHANCE)) { + type = Lives.Type.TEAM; + lives *= match.module(TeamMatchModule.class).get().getFullestTeam().getSize(); + } else { + type = Lives.Type.INDIVIDUAL; + } + match.module(BlitzMatchModuleImpl.class).get().activate(BlitzProperties.create(match, lives, type)); + } + + @Override + public void disable() { + match.getScheduler(MatchScope.LOADED).createTask(() -> { + if(!match.isFinished()) { + match.module(BlitzMatchModuleImpl.class).get().deactivate(); + } + }); + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java b/PGM/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java index bc9fe51..809bdea 100644 --- a/PGM/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java @@ -40,7 +40,7 @@ import tc.oc.commons.core.chat.ChatUtils; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.formatting.StringUtils; import tc.oc.pgm.PGMTranslations; -import tc.oc.pgm.blitz.BlitzModule; +import tc.oc.pgm.blitz.BlitzEvent; import tc.oc.pgm.classes.ClassMatchModule; import tc.oc.pgm.classes.ClassModule; import tc.oc.pgm.classes.PlayerClass; @@ -53,6 +53,7 @@ import tc.oc.pgm.events.PlayerPartyChangeEvent; import tc.oc.pgm.join.JoinMatchModule; import tc.oc.pgm.join.JoinRequest; import tc.oc.pgm.join.JoinResult; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.MatchScope; @@ -94,18 +95,18 @@ public class PickerMatchModule extends MatchModule implements Listener { private final ComponentRenderContext renderer; private final JoinMatchModule jmm; + private final BlitzMatchModule bmm; private final boolean hasTeams; private final boolean hasClasses; - private final boolean isBlitz; private final Set picking = new HashSet<>(); - @Inject PickerMatchModule(ComponentRenderContext renderer, JoinMatchModule jmm, Optional teamModule, Optional classModule, Optional blitzModule) { + @Inject PickerMatchModule(ComponentRenderContext renderer, JoinMatchModule jmm, BlitzMatchModule bmm, Optional teamModule, Optional classModule) { this.renderer = renderer; this.jmm = jmm; + this.bmm = bmm; this.hasTeams = teamModule.isPresent(); this.hasClasses = classModule.isPresent(); - this.isBlitz = blitzModule.filter(BlitzModule::isEnabled).isPresent(); } protected boolean settingEnabled(MatchPlayer player) { @@ -142,7 +143,7 @@ public class PickerMatchModule extends MatchModule implements Listener { if(player == null) return false; // Player is eliminated from Blitz - if(isBlitz && getMatch().isRunning()) return false; + if(bmm.activated() && getMatch().isRunning()) return false; // Player is not observing or dead if(!(player.isObserving() || player.isDead())) return false; @@ -346,6 +347,12 @@ public class PickerMatchModule extends MatchModule implements Listener { refreshKitAll(); } + @EventHandler + public void blitzEnable(final BlitzEvent event) { + refreshCountsAll(); + refreshKitAll(); + } + /** * Open the window for the given player, or refresh its contents * if they already have it open, and return the current contents. diff --git a/PGM/src/main/java/tc/oc/pgm/rage/RageModule.java b/PGM/src/main/java/tc/oc/pgm/rage/RageModule.java index ea26d2f..ecb8fd8 100644 --- a/PGM/src/main/java/tc/oc/pgm/rage/RageModule.java +++ b/PGM/src/main/java/tc/oc/pgm/rage/RageModule.java @@ -47,7 +47,7 @@ public class RageModule implements MapModule, MatchModuleFactory { private final ScoreConfig config; diff --git a/PGM/src/main/java/tc/oc/pgm/scoreboard/SidebarMatchModule.java b/PGM/src/main/java/tc/oc/pgm/scoreboard/SidebarMatchModule.java index 55d2797..ced0f09 100644 --- a/PGM/src/main/java/tc/oc/pgm/scoreboard/SidebarMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/scoreboard/SidebarMatchModule.java @@ -31,7 +31,7 @@ import tc.oc.commons.bukkit.util.NullCommandSender; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.scheduler.Task; import tc.oc.pgm.Config; -import tc.oc.pgm.blitz.BlitzMatchModule; +import tc.oc.pgm.blitz.LivesEvent; import tc.oc.pgm.destroyable.Destroyable; import tc.oc.pgm.events.FeatureChangeEvent; import tc.oc.pgm.events.ListenerScope; @@ -50,6 +50,9 @@ import tc.oc.pgm.goals.events.GoalCompleteEvent; import tc.oc.pgm.goals.events.GoalProximityChangeEvent; import tc.oc.pgm.goals.events.GoalStatusChangeEvent; import tc.oc.pgm.goals.events.GoalTouchEvent; +import tc.oc.pgm.blitz.Lives; +import tc.oc.pgm.blitz.BlitzEvent; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.Competitor; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchModule; @@ -72,6 +75,7 @@ public class SidebarMatchModule extends MatchModule implements Listener { public static final int MAX_SUFFIX = 16; // Max chars in a team suffix @Inject private List wools; + @Inject private BlitzMatchModule blitz; private final String legacyTitle; @@ -172,8 +176,8 @@ public class SidebarMatchModule extends MatchModule implements Listener { return getMatch().getMatchModule(ScoreMatchModule.class) != null; } - private boolean isBlitz() { - return getMatch().getMatchModule(BlitzMatchModule.class) != null; + private boolean lives(Lives.Type type) { + return blitz.activated() && blitz.properties().type.equals(type); } private boolean isCompactWool() { @@ -290,6 +294,16 @@ public class SidebarMatchModule extends MatchModule implements Listener { renderSidebarDebounce(); } + @EventHandler(priority = EventPriority.MONITOR) + public void blitzEnable(BlitzEvent event) { + renderSidebarDebounce(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void livesChange(LivesEvent event) { + renderSidebarDebounce(); + } + private String renderGoal(Goal goal, @Nullable Competitor competitor, Party viewingParty) { StringBuilder sb = new StringBuilder(" "); @@ -325,11 +339,10 @@ public class SidebarMatchModule extends MatchModule implements Listener { } private String renderBlitz(Competitor competitor, Party viewingParty) { - BlitzMatchModule bmm = getMatch().needMatchModule(BlitzMatchModule.class); if(competitor instanceof tc.oc.pgm.teams.Team) { - return ChatColor.WHITE.toString() + bmm.getRemainingPlayers(competitor); - } else if(competitor instanceof Tribute && bmm.getConfig().getNumLives() > 1) { - return ChatColor.WHITE.toString() + bmm.lifeManager.getLives(competitor.getPlayers().iterator().next().getPlayerId()); + return ChatColor.WHITE.toString() + competitor.getPlayers().size(); + } else if(competitor instanceof Tribute && blitz.properties().multipleLives()) { + return ChatColor.WHITE.toString() + blitz.livesCount(competitor.getPlayers().iterator().next()); } else { return ""; } @@ -341,7 +354,8 @@ public class SidebarMatchModule extends MatchModule implements Listener { private void renderSidebar() { final boolean hasScores = hasScores(); - final boolean isBlitz = isBlitz(); + final boolean hasIndividualLives = lives(Lives.Type.INDIVIDUAL); + final boolean hasTeamLives = lives(Lives.Type.TEAM); final GoalMatchModule gmm = match.needMatchModule(GoalMatchModule.class); Set competitorsWithGoals = new HashSet<>(); @@ -367,7 +381,7 @@ public class SidebarMatchModule extends MatchModule implements Listener { List rows = new ArrayList<>(MAX_ROWS); // Scores/Blitz - if(hasScores || isBlitz) { + if(hasScores || hasIndividualLives || (hasTeamLives && competitorsWithGoals.isEmpty())) { for(Competitor competitor : getMatch().needMatchModule(VictoryMatchModule.class).rankedCompetitors()) { String text; if(hasScores) { @@ -419,6 +433,11 @@ public class SidebarMatchModule extends MatchModule implements Listener { rows.add(ComponentRenderers.toLegacyText(competitor.getStyledName(NameStyle.GAME), NullCommandSender.INSTANCE)); + // Add lives status under the team name + if(hasTeamLives) { + blitz.lives(competitor).ifPresent(l -> rows.add(ComponentRenderers.toLegacyText(l.status(), NullCommandSender.INSTANCE))); + } + if(isCompactWool()) { String woolText = " "; boolean firstWool = true; diff --git a/PGM/src/main/java/tc/oc/pgm/teams/TeamFactory.java b/PGM/src/main/java/tc/oc/pgm/teams/TeamFactory.java index 3145412..54e9446 100644 --- a/PGM/src/main/java/tc/oc/pgm/teams/TeamFactory.java +++ b/PGM/src/main/java/tc/oc/pgm/teams/TeamFactory.java @@ -77,6 +77,9 @@ public interface TeamFactory extends SluggedFeatureDefinition, Validatable, Feat return org.bukkit.scoreboard.Team.OptionStatus.ALWAYS; } + @Property(name="lives") + Optional getLives(); + MapDoc.Team getDocument(); @Override @@ -84,6 +87,9 @@ public interface TeamFactory extends SluggedFeatureDefinition, Validatable, Feat if(getMaxOverfill().isPresent() && getMaxOverfill().get() < getMaxPlayers()) { throw new InvalidXMLException("Max overfill cannot be less than max players"); } + if(getLives().isPresent() && getLives().get() <= 0) { + throw new InvalidXMLException("Lives must be at least 1"); + } } } diff --git a/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java b/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java index 6be49ac..b0c8b21 100644 --- a/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java +++ b/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java @@ -26,10 +26,11 @@ import tc.oc.api.docs.Entrant; import tc.oc.api.docs.Tournament; import tc.oc.commons.bukkit.event.UserLoginEvent; import tc.oc.commons.core.chat.Component; -import tc.oc.pgm.blitz.BlitzMatchModule; +import tc.oc.pgm.blitz.BlitzMatchModuleImpl; import tc.oc.pgm.channels.ChannelMatchModule; import tc.oc.pgm.events.MatchEndEvent; import tc.oc.pgm.events.MatchPlayerAddEvent; +import tc.oc.pgm.blitz.BlitzMatchModule; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.MatchState; @@ -120,7 +121,7 @@ public class TeamListener implements Listener { TourneyState state = event.getNewState(); if(tourney.getKDMSession() == null && state.equals(TourneyState.ENABLED_WAITING_FOR_READY)) { Match match = matchProvider.get(); - if(match.getMatchModule(ScoreMatchModule.class) != null || match.getMatchModule(BlitzMatchModule.class) != null) { + if(match.getMatchModule(ScoreMatchModule.class) != null || match.module(BlitzMatchModuleImpl.class).filter(BlitzMatchModule::activated).isPresent()) { tourney.createKDMSession(); } } diff --git a/Util/core/src/main/java/tc/oc/commons/core/formatting/StringUtils.java b/Util/core/src/main/java/tc/oc/commons/core/formatting/StringUtils.java index 594ca97..8ac183e 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/formatting/StringUtils.java +++ b/Util/core/src/main/java/tc/oc/commons/core/formatting/StringUtils.java @@ -179,6 +179,10 @@ public class StringUtils { return builder.toString(); } + public static List complete(String prefix, Class enumClass) { + return complete(prefix, Stream.of(enumClass.getEnumConstants()).map(c -> c.name().toLowerCase().replace('_', ' '))); + } + public static List complete(String prefix, Collection options) { return complete(prefix, options.stream()); } From 89a9954075e4091fc225255cdea914737574efd3 Mon Sep 17 00:00:00 2001 From: ShinyDialga Date: Fri, 31 Mar 2017 20:48:01 -0500 Subject: [PATCH 010/252] Update shop link to stratus buycraft --- .../main/java/tc/oc/commons/bukkit/chat/Links.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Links.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Links.java index d9ccc9b..5d7a0a8 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Links.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Links.java @@ -13,12 +13,17 @@ import tc.oc.commons.core.util.ExceptionUtils; public class Links { private Links() {} - public static final String HOST = "localhost"; // TODO: configurable + public static final String HOST = "stratus.network"; + public static final String SHOP_HOST = "stratusnetwork.buycraft.net"; public static URI homeUri(String path) throws URISyntaxException { return new URI("http", HOST, path, null); } + public static URI shopUri() throws URISyntaxException { + return new URI("http", SHOP_HOST, null, null); + } + public static URI homeUriSafe(String path) { return ExceptionUtils.propagate(() -> homeUri(path)); } @@ -39,8 +44,12 @@ public class Links { return homeLinkSafe("/"); } + public static BaseComponent shopLink(boolean compact) throws URISyntaxException { + return new LinkComponent(shopUri(), compact); + } + public static BaseComponent shopLink() { - return homeLinkSafe("/shop"); + return ExceptionUtils.propagate(() -> shopLink(true)); } public static BaseComponent appealLink() { From 8a3269c678fdf895dabc8c08af5b9913a82b3280 Mon Sep 17 00:00:00 2001 From: Electroid Date: Sat, 1 Apr 2017 12:47:54 -0700 Subject: [PATCH 011/252] Fix error when opening player inventories --- .../main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java b/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java index ffb82f7..c335ebe 100644 --- a/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/inventory/ViewInventoryMatchModule.java @@ -270,7 +270,7 @@ public class ViewInventoryMatchModule extends MatchModule implements Listener { MatchPlayer matchHolder = this.match.getPlayer(holder); if (matchHolder != null && matchHolder.isParticipating()) { BlitzMatchModule module = matchHolder.getMatch().getMatchModule(BlitzMatchModuleImpl.class); - if(module != null) { + if(module != null && module.activated() && module.lives(matchHolder).isPresent()) { int livesLeft = module.livesCount(matchHolder); ItemStack lives = new ItemStack(Material.EGG, livesLeft); ItemMeta lifeMeta = lives.getItemMeta(); From e03f3a980c34a8f554647a17651440ad2919680b Mon Sep 17 00:00:00 2001 From: Electroid Date: Sun, 2 Apr 2017 19:09:35 -0700 Subject: [PATCH 012/252] Fix edge case errors with mutations --- .../i18n/templates/pgm/PGMMessages.properties | 4 + .../tc/oc/pgm/blitz/BlitzMatchModuleImpl.java | 31 ++- .../main/java/tc/oc/pgm/blitz/LivesBase.java | 7 +- .../main/java/tc/oc/pgm/blitz/LivesTeam.java | 23 ++ .../tc/oc/pgm/modules/MobsMatchModule.java | 7 +- .../oc/pgm/mutation/MutationMatchModule.java | 14 - .../oc/pgm/mutation/types/EntityMutation.java | 239 ++++++++++++++++++ .../tc/oc/pgm/mutation/types/KitMutation.java | 36 ++- .../oc/pgm/mutation/types/MutationModule.java | 95 +++---- .../oc/pgm/mutation/types/TargetMutation.java | 102 ++++++-- .../mutation/types/kit/ElytraMutation.java | 72 +++++- .../types/kit/EnchantmentMutation.java | 20 +- .../types/kit/EquestrianMutation.java | 96 +++---- .../mutation/types/kit/ExplosiveMutation.java | 21 +- .../mutation/types/kit/HardcoreMutation.java | 7 +- .../mutation/types/kit/HealthMutation.java | 1 - .../pgm/mutation/types/kit/MobsMutation.java | 13 +- .../mutation/types/kit/NoFallMutation.java | 2 +- .../mutation/types/kit/PotionMutation.java | 12 +- .../types/kit/ProjectileMutation.java | 37 +-- .../mutation/types/other/BlitzMutation.java | 16 +- .../mutation/types/other/RageMutation.java | 3 +- .../types/targetable/ApocalypseMutation.java | 106 ++++---- .../types/targetable/BomberMutation.java | 66 +++-- .../types/targetable/LightningMutation.java | 18 +- 25 files changed, 702 insertions(+), 346 deletions(-) create mode 100644 PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index 6025635..00e3149 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -198,6 +198,7 @@ mutation.type.explosive = Explosive mutation.type.explosive.desc = tnt and fire bows mutation.type.elytra = Elytra mutation.type.elytra.desc = fly around with an elytra +mutation.type.elytra.land = Land immediately. Your elytra is being grounded in {0} seconds. mutation.type.projectile = Projectile mutation.type.projectile.desc = arrow potion effects mutation.type.enchantment = Enchantment @@ -268,6 +269,9 @@ lives.remaining.individual.plural = You have {0} lives left lives.remaining.team.singular = Your team has {0} life left lives.remaining.team.plural = Your team has {0} lives left +lives.remaining.alive.singular = Your team has {0} player left +lives.remaining.alive.plural = Your team has {0} players left + lives.status.eliminated = eliminated lives.status.alive = {0} alive lives.status.lives = {0} lives diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java index cd7bad7..8f0ae0c 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java @@ -105,7 +105,11 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul } } - private void showLives(MatchPlayer player, boolean release, boolean activate) { + private void livesHotbar(MatchPlayer player) { + lives(player).map(Lives::remaining).ifPresent(player::sendHotbarMessage); + } + + private void livesTitle(MatchPlayer player, boolean release, boolean activate) { final Optional lives = lives(player); if(activated() && lives.isPresent()) { player.showTitle( @@ -118,6 +122,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul } } + private void update() { + match.callEvent(new BlitzEvent(match, this)); + } + @Override public boolean activated() { return activated; @@ -142,6 +150,7 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul activated = false; lives.clear(); eliminated.clear(); + update(); } @Override @@ -161,10 +170,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul match.participants().forEach(player -> { setup(player, false); if(match.hasStarted()) { - showLives(player, false, true); + livesTitle(player, false, true); } }); - match.callEvent(new BlitzEvent(match, this)); + update(); } } @@ -181,6 +190,7 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul if(notify) { player.showTitle(Components.blank(), life.change(lives), 0, 40, 10); } + player.competitor().ifPresent(competitor -> competitor.participants().forEach(this::livesHotbar)); if(life.empty() && immediate) { eliminate(player); return true; @@ -225,9 +235,9 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul ImmutableSet.copyOf(getMatch().getParticipatingPlayers()) .stream() .filter(this::eliminated) - .forEach(participating -> { - match.setPlayerParty(participating, match.getDefaultParty()); - world.spawnParticle(Particle.SMOKE_LARGE, player.getLocation(), 5); + .forEach(eliminated -> { + match.setPlayerParty(eliminated, match.getDefaultParty()); + world.spawnParticle(Particle.SMOKE_LARGE, eliminated.getLocation(), 5); }); victory.invalidateAndCheckEnd(); }); @@ -269,15 +279,14 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onDeath(MatchPlayerDeathEvent event) { - final MatchPlayer player = event.getVictim(); - if(player.competitor().isPresent()) { - increment(player, -1, false, true); - } + event.getVictim() + .competitor() + .ifPresent(competitor -> increment(event.getVictim(), -1, false, true)); } @EventHandler public void onRelease(ParticipantReleaseEvent event) { - showLives(event.getPlayer(), event.wasFrozen(), false); + livesTitle(event.getPlayer(), event.wasFrozen(), false); } } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java index 77d7154..77d2482 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java @@ -27,7 +27,7 @@ public abstract class LivesBase implements Lives { update(); } - private void update() { + protected void update() { competitor().getMatch().callEvent(new LivesEvent(this)); } @@ -67,7 +67,8 @@ public abstract class LivesBase implements Lives { public BaseComponent remaining() { return new Component( new TranslatableComponent( - "lives.remaining." + type().name().toLowerCase() + "." + (current() == 1 ? "singular" : "plural"), + "lives.remaining." + type().name().toLowerCase() + "." + (current() == 1 ? "singular" + : "plural"), new Component(current(), ChatColor.YELLOW) ), ChatColor.AQUA @@ -103,7 +104,7 @@ public abstract class LivesBase implements Lives { new TranslatableComponent( (delta > 0 ? "lives.change.gained." : "lives.change.lost.") + (absDelta == 1 ? "singular" - : "plural"), + : "plural"), new Component(absDelta, ChatColor.AQUA) ), ChatColor.WHITE diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java index f989a87..ade9827 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java @@ -1,6 +1,11 @@ package tc.oc.pgm.blitz; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; import tc.oc.api.docs.PlayerId; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; import tc.oc.pgm.match.Competitor; public class LivesTeam extends LivesBase { @@ -24,6 +29,24 @@ public class LivesTeam extends LivesBase { return false; } + public int alive() { + return (int) competitor().participants().count(); + } + + @Override + public BaseComponent remaining() { + int alive = alive() - 1; + if(alive == 0) return Components.blank(); + return empty() ? new Component( + new TranslatableComponent( + "lives.remaining.alive." + (alive == 1 ? "singular" + : "plural"), + new Component(alive, ChatColor.YELLOW) + ), + ChatColor.AQUA + ) : super.remaining(); + } + @Override public int hashCode() { return competitor().hashCode(); diff --git a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java index 500ae8b..e4bf86c 100644 --- a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java @@ -1,6 +1,7 @@ package tc.oc.pgm.modules; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.pgm.events.ListenerScope; @@ -9,7 +10,6 @@ import tc.oc.pgm.filters.Filter; import tc.oc.pgm.filters.query.EntitySpawnQuery; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.mutation.MutationMatchModule; @ListenerScope(MatchScope.LOADED) public class MobsMatchModule extends MatchModule implements Listener { @@ -37,10 +37,9 @@ public class MobsMatchModule extends MatchModule implements Listener { getMatch().getWorld().setSpawnFlags(false, false); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void checkSpawn(final CreatureSpawnEvent event) { - if(!match.module(MutationMatchModule.class).map(mmm -> mmm.allowMob(event.getSpawnReason())).orElse(false) && - event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { + if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { event.setCancelled(mobsFilter.query(new EntitySpawnQuery(event, event.getEntity(), event.getSpawnReason())).isDenied()); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java index db3046f..baf8ef7 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java @@ -4,7 +4,6 @@ import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.commons.core.util.MapUtils; import tc.oc.commons.core.random.RandomUtils; import tc.oc.pgm.Config; @@ -157,17 +156,4 @@ public class MutationMatchModule extends MatchModule { return mutationsActive().stream().anyMatch(m1 -> Stream.of(mutations).anyMatch(m2 -> m2.equals(m1))); } - public boolean allowMob(CreatureSpawnEvent.SpawnReason reason) { - switch(reason) { - case NATURAL: - case DEFAULT: - case CHUNK_GEN: - case JOCKEY: - case MOUNT: - return false; - default: - return true; - } - } - } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java new file mode 100644 index 0000000..19e534c --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java @@ -0,0 +1,239 @@ +package tc.oc.pgm.mutation.types; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.gson.reflect.TypeToken; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.player.PlayerSpawnEntityEvent; +import org.bukkit.inventory.EntityEquipment; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.pgm.events.MatchPlayerDeathEvent; +import tc.oc.pgm.events.PlayerChangePartyEvent; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; + +import javax.annotation.Nullable; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Stream; + +import static tc.oc.commons.core.util.Optionals.cast; + +/** + * A mutation module that tracks entity spawns. + */ +public class EntityMutation extends KitMutation { + + final Set entities; + final Map> entitiesByTime; + final Map> entitiesByPlayer; + final Map playersByEntity; + + public EntityMutation(Match match, boolean force) { + super(match, force); + this.entities = new WeakHashSet<>(); + this.entitiesByTime = new WeakHashMap<>(); + this.entitiesByPlayer = new WeakHashMap<>(); + this.playersByEntity = new WeakHashMap<>(); + } + + /** + * Gets an immutable stream of all registered entities. + * @return stream of entities. + */ + public Stream entities() { + return ImmutableSet.copyOf(entities).stream(); + } + + /** + * Gets an immutable stream of all registered entities + * by the time they spawned in ascending order. + * @return stream of entities. + */ + public Stream entitiesByTime() { + return ImmutableMap.copyOf(entitiesByTime) + .entrySet() + .stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(Instant::toEpochMilli))) + .flatMap(entry -> ImmutableSet.copyOf(entry.getValue()).stream()); + } + + /** + * Gets an immutable stream of all registered entities + * that are owned by a player. + * + * If the given player is null, this will return entities + * with no owner. + * @param player the optional player. + * @return stream of entities. + */ + public Stream entitiesByPlayer(@Nullable MatchPlayer player) { + return ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).stream(); + } + + /** + * Gets the optional owner of an entity. + * @param entity the entity to find the owner of. + * @return the optional player. + */ + public Optional playerByEntity(Entity entity) { + return Optional.ofNullable(playersByEntity.get(entity)); + } + + /** + * Are entities with this spawn reason allowed to spawn? + * @param reason the spawn reason. + * @return whether this reason is allowed. + */ + public boolean allowed(CreatureSpawnEvent.SpawnReason reason) { + switch(reason) { + case NATURAL: + case DEFAULT: + case CHUNK_GEN: + case JOCKEY: + case MOUNT: + return false; + default: + return true; + } + } + + /** + * Register a new spawned entity with an optional owner. + * @param entity the entity to register. + * @param owner the optional owner. + * @return the entity. + */ + public E register(E entity, @Nullable MatchPlayer owner) { + entities.add(entity); + final Instant now = match().getInstantNow(); + final Set byTime = entitiesByTime.getOrDefault(now, new WeakHashSet<>()); + byTime.add(entity); + entitiesByTime.put(now, byTime); + if(owner != null) { + final Set byPlayer = entitiesByPlayer.getOrDefault(owner, new WeakHashSet<>()); + byPlayer.add(entity); + entitiesByPlayer.put(owner, byPlayer); + playersByEntity.put(entity, owner); + } + return entity; + } + + /** + * Removes the entity from the world. + * + * Typically this should be {@link Entity#remove()}, + * but it can also expire the entity after a couple of seconds. + * @param entity the entity to remove. + */ + public void remove(E entity) { + entity.remove(); + } + + /** + * Unregister and remove the given entity. + * @param entity the entity. + */ + public void despawn(E entity) { + entities.remove(entity); + playersByEntity.remove(entity); + Stream.of(entitiesByTime, entitiesByPlayer) + .flatMap(map -> ImmutableList.copyOf(map.values()).stream()) + .forEach(set -> set.remove(entity)); + remove(entity); + } + + /** + * Unregister and remove any entities owned by the given player. + * @param player the owner of the entities. + */ + public void despawn(MatchPlayer player) { + ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).forEach(this::despawn); + } + + /** + * Spawn an entity at the given location with no owner. + * @see #spawn(Location, Class, MatchPlayer) + * @return the entity. + */ + public E spawn(Location location, Class entityClass) { + return spawn(location, entityClass, null); + } + + /** + * Spawn an entity at the given location. + * @param location the location to spawn the entity. + * @param entityClass the class of the entity. + * @param owner the optional owner of the entity. + * @return the entity. + */ + public E spawn(Location location, Class entityClass, @Nullable MatchPlayer owner) { + E entity = world().spawn(location, entityClass); + cast(entity, LivingEntity.class).ifPresent(living -> { + living.setCanPickupItems(false); + living.setRemoveWhenFarAway(true); + EntityEquipment equipment = living.getEquipment(); + equipment.setHelmetDropChance(0); + equipment.setChestplateDropChance(0); + equipment.setLeggingsDropChance(0); + equipment.setBootsDropChance(0); + }); + return register(entity, owner); + } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST) + public void onPlayerSpawnEntity(PlayerSpawnEntityEvent event) { + match().participant(event.getPlayer()) + .ifPresent(player -> cast(event.getEntity(), new TypeToken(){}.getRawType()) + .ifPresent(entity -> { + register((E) entity, player); + event.setCancelled(false); + })); + } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST) + public void onEntitySpawn(CreatureSpawnEvent event) { + boolean allowed = allowed(event.getSpawnReason()); + event.setCancelled(!allowed); + if(allowed) { + cast(event.getEntity(), new TypeToken(){}.getRawType()) + .filter(entity -> !playerByEntity((E) entity).isPresent()) + .ifPresent(entity -> register((E) entity, null)); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerDeath(MatchPlayerDeathEvent event) { + despawn(event.getVictim()); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPartyChange(PlayerChangePartyEvent event) { + despawn(event.getPlayer()); + } + + @Override + public void remove(MatchPlayer player) { + despawn(player); + super.remove(player); + } + + @Override + public void disable() { + entities().forEach(this::despawn); + Stream.of(entitiesByTime, entitiesByPlayer, playersByEntity).forEach(Map::clear); + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java index 813022b..18dd1e6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java @@ -1,9 +1,16 @@ package tc.oc.pgm.mutation.types; +import org.bukkit.Material; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.inventory.ItemStack; import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.core.util.Optionals; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.killreward.KillRewardMatchModule; +import tc.oc.pgm.kits.ItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.kits.KitPlayerFacet; import tc.oc.pgm.match.Match; @@ -11,19 +18,22 @@ import tc.oc.pgm.match.MatchPlayer; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import java.util.stream.Stream; /** * A mutation module that injects special kits on spawn and/or kill. */ -public class KitMutation extends MutationModule { +public class KitMutation extends MutationModule.Impl { protected final List kits; protected final Map> playerKits; protected final Map> savedSlots; + protected final Set itemRemove; protected final List rewards; protected final boolean force; @@ -32,6 +42,7 @@ public class KitMutation extends MutationModule { this.kits = new ArrayList<>(); this.playerKits = new WeakHashMap<>(); this.savedSlots = new WeakHashMap<>(); + this.itemRemove = new HashSet<>(); this.rewards = new ArrayList<>(); this.force = force; } @@ -59,6 +70,7 @@ public class KitMutation extends MutationModule { List kits = new ArrayList<>(); kits(player, kits); playerKits.put(player, kits); + kits.forEach(kit -> Optionals.cast(kit, ItemKit.class).ifPresent(items -> itemRemove.add(items.item().getType()))); saved().forEach(slot -> { slot.item(player.getInventory()).ifPresent(item -> { Map slots = savedSlots.getOrDefault(player, new HashMap<>()); @@ -88,22 +100,34 @@ public class KitMutation extends MutationModule { return Stream.empty(); } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onItemDrop(ItemSpawnEvent event) { + Item entity = event.getEntity(); + if(entity != null) { + ItemStack item = entity.getItemStack(); + if(item != null && itemRemove.contains(item.getType())) { + entity.remove(); + } + } + } + @Override public void enable() { super.enable(); - match.module(KillRewardMatchModule.class).get().rewards().addAll(rewards); - if(match.hasStarted()) { - match.participants().forEach(this::apply); + match().module(KillRewardMatchModule.class).get().rewards().addAll(rewards); + if(match().hasStarted()) { + match().participants().forEach(this::apply); } } @Override public void disable() { - match.module(KillRewardMatchModule.class).get().rewards().removeAll(rewards); - match.participants().forEach(this::remove); + match().module(KillRewardMatchModule.class).get().rewards().removeAll(rewards); + match().participants().forEach(this::remove); kits.clear(); playerKits.clear(); savedSlots.clear(); + itemRemove.clear(); rewards.clear(); super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java index 712073f..b3458d6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java @@ -1,12 +1,8 @@ package tc.oc.pgm.mutation.types; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; import org.bukkit.event.Listener; -import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; import tc.oc.commons.bukkit.item.ItemBuilder; import tc.oc.commons.core.random.AdvancingEntropy; @@ -15,7 +11,6 @@ import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.mutation.Mutation; -import tc.oc.pgm.mutation.MutationMatchModule; import java.util.Random; @@ -28,69 +23,59 @@ import java.util.Random; * of breaking the match state. */ @ListenerScope(MatchScope.RUNNING) -public abstract class MutationModule implements Listener { +public interface MutationModule extends Listener { - protected final Match match; - protected final World world; - protected final Entropy entropy; - protected final Random random; + Match match(); - /** - * Constructed when {@link MutationMatchModule#load()} - * has been called. This will only be constructed if its - * subsequent {@link Mutation} is enabled for the match. - * - * @param match the match for this module. - */ - public MutationModule(Match match) { - this.match = match; - this.world = match.getWorld(); - this.entropy = new AdvancingEntropy(); - this.random = new Random(); + default void enable() { + match().registerEventsAndRepeatables(this); } - /** - * Called when the match starts. - * - * However, this should be able to be called at any - * point before the match ends and still work as expected. - */ - public void enable() { - match.registerEventsAndRepeatables(this); + default void disable() { + match().unregisterEvents(this); + match().unregisterRepeatable(this); } - /** - * Called when the match ends. - * - * However, this should be able to be called at any - * point during a match and still work as expected. - */ - public void disable() { - match.unregisterEvents(this); - match.unregisterRepeatable(this); + default World world() { + return match().getWorld(); } - protected static ItemStack item(Material material, int amount) { - return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get(); + default Entropy entropy() { + return match().entropyForTick(); } - protected static ItemStack item(Material material) { - return item(material, 1); + default Random random() { + return match().getRandom(); } - protected E spawn(Location location, Class entityClass) { - E entity = world.spawn(location, entityClass); - if(entity instanceof LivingEntity) { - LivingEntity living = (LivingEntity) entity; - living.setCanPickupItems(false); - living.setRemoveWhenFarAway(true); - EntityEquipment equipment = living.getEquipment(); - equipment.setHelmetDropChance(0); - equipment.setChestplateDropChance(0); - equipment.setLeggingsDropChance(0); - equipment.setBootsDropChance(0); + abstract class Impl implements MutationModule { + + private final Match match; + private final Entropy entropy; + + public Impl(final Match match) { + this.match = match; + this.entropy = new AdvancingEntropy(match.entropyForTick().randomLong()); } - return entity; + + @Override + public Match match() { + return match; + } + + @Override + public Entropy entropy() { + return entropy; + } + + protected static ItemStack item(Material material, int amount) { + return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get(); + } + + protected static ItemStack item(Material material) { + return item(material, 1); + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java index 96f61f7..5e7371a 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java @@ -1,64 +1,114 @@ package tc.oc.pgm.mutation.types; import java.time.Duration; +import java.time.Instant; import java.util.List; -import tc.oc.commons.core.scheduler.Task; import tc.oc.commons.core.stream.Collectors; +import tc.oc.commons.core.util.TimeUtils; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.match.Repeatable; +import tc.oc.time.Time; /** * A mutation module that executes a task on random {@link MatchPlayer}s. */ -public abstract class TargetMutation extends MutationModule { - - private final Duration frequency; - private Task task; - - public TargetMutation(final Match match, Duration frequency) { - super(match); - this.frequency = frequency; - } +public interface TargetMutation extends MutationModule { /** * Execute a task on the given randomly selected players. * @param players a list of players, which size is determined by {@link #targets()}. */ - public abstract void execute(List players); + void target(List players); /** * Determine the number of random players to target. + * * If there are no enough players on the server, it is possible * that the number of targets is less than expected. * @return number of targets. */ - public abstract int targets(); + int targets(); + + /** + * Get the next time {@link #target()} will be run. + * @return next target time. + */ + Instant next(); + + /** + * Set the next time {@link #target()} will be run. + * @param time next target time. + */ + void next(Instant time); + + /** + * Get the frequency that {@link #target()} will be run. + * @return frequency between target times. + */ + Duration frequency(); /** * Generate a list of random players. * @return the random players. */ - public List search() { - return match.participants() - .filter(MatchPlayer::isSpawned) - .collect(Collectors.toRandomSubList(entropy, targets())); + default List search() { + return match().participants() + .filter(MatchPlayer::isSpawned) + .collect(Collectors.toRandomSubList(entropy(), targets())); + } + + /** + * Execute a task on randomly selected players and reset the + * next time the task will be executed. + */ + default void target() { + target(search()); + next(match().getInstantNow().plus(frequency())); } @Override - public void enable() { - super.enable(); - this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> execute(search())); + default void enable() { + MutationModule.super.enable(); + target(); } - @Override - public void disable() { - if(task != null) { - task.cancel(); - task = null; + @Repeatable(interval = @Time(seconds = 1)) + default void tick() { + Instant now = match().getInstantNow(), next = next(); + if(next == null) { + next(now.plus(frequency())); + } else if(TimeUtils.isEqualOrBeforeNow(now, next)) { + target(); } - super.disable(); + } + + abstract class Impl extends MutationModule.Impl implements TargetMutation { + + Duration frequency; + Instant next; + + public Impl(Match match, Duration frequency) { + super(match); + this.frequency = frequency; + } + + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return frequency; + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java index 1040b95..3f832f6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java @@ -1,17 +1,31 @@ package tc.oc.pgm.mutation.types.kit; +import com.google.common.collect.ImmutableSet; +import net.md_5.bungee.api.ChatColor; import org.bukkit.Material; +import tc.oc.commons.bukkit.chat.WarningComponent; import tc.oc.commons.bukkit.inventory.ArmorType; import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.commons.core.util.TimeUtils; import tc.oc.pgm.doublejump.DoubleJumpKit; import tc.oc.pgm.kits.ItemKit; -import tc.oc.pgm.kits.ItemKitApplicator; +import tc.oc.pgm.kits.KitNode; +import tc.oc.pgm.kits.KitPlayerFacet; +import tc.oc.pgm.kits.RemoveKit; import tc.oc.pgm.kits.SlotItemKit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.mutation.Mutation; +import tc.oc.pgm.mutation.MutationMatchModule; import tc.oc.pgm.mutation.types.KitMutation; import java.time.Duration; +import java.time.Instant; +import java.util.stream.Collectors; import java.util.stream.Stream; public class ElytraMutation extends KitMutation { @@ -30,13 +44,57 @@ public class ElytraMutation extends KitMutation { @Override public void remove(MatchPlayer player) { - // If the player is mid-air, give them a totem so they don't fall and die - if(player.getBukkit().isGliding()) { - ItemKitApplicator applicator = new ItemKitApplicator(); - applicator.put(Slot.OffHand.offHand(), item(Material.TOTEM), false); - applicator.apply(player); + // Anyone left gliding will be taken care of by the ground stop order + if(!player.getBukkit().isGliding()) { + super.remove(player); } - super.remove(player); + } + + @Override + public void disable() { + new GroundStop(match()).run(); + super.disable(); + } + + /** + * A cleanup task to slowly remove elytras from players. + * + * This prevents players that are mid-glide from falling + * out of the sky and gives them time to land. + */ + private class GroundStop implements Runnable { + + Match match; + WeakHashSet gliding; + Instant end; + + GroundStop(Match match) { + this.match = match; + this.gliding = new WeakHashSet<>(match.participants().filter(player -> player.getBukkit().isGliding()).collect(Collectors.toSet())); + this.end = match.getInstantNow().plus(Duration.ofSeconds(10)); + } + + @Override + public void run() { + if(match.isRunning() && !gliding.isEmpty() && !match.module(MutationMatchModule.class).get().enabled(Mutation.ELYTRA)) { + Instant now = match().getInstantNow(); + for(MatchPlayer player : ImmutableSet.copyOf(gliding)) { + if(!player.isSpawned() || !player.getBukkit().isGliding() || TimeUtils.isEqualOrBeforeNow(now, end)) { + gliding.remove(player); + player.facet(KitPlayerFacet.class).applyKit(KitNode.of(new RemoveKit(ELYTRA), new RemoveKit(JUMP)), true); + player.sendHotbarMessage(Components.blank()); + } else { + long seconds = Duration.between(now, end).getSeconds(); + player.sendHotbarMessage(new WarningComponent("mutation.type.elytra.land", new Component(seconds, ChatColor.YELLOW))); + } + } + match.getScheduler(MatchScope.RUNNING).createDelayedTask(Duration.ofMillis(50), this::run); + } else { + gliding.clear(); + match = null; + } + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java index 7675061..12f8819 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java @@ -21,31 +21,28 @@ import java.util.WeakHashMap; public class EnchantmentMutation extends KitMutation { final static ImmutableMap LEVELS_MAP = new ImmutableMap.Builder() - .put(1, 10) - .put(2, 3) + .put(1, 25) + .put(2, 5) .put(3, 1) .build(); final static ImmutableMap ARMOR_MAP = new ImmutableMap.Builder() .put(Enchantment.PROTECTION_ENVIRONMENTAL, 15) .put(Enchantment.PROTECTION_PROJECTILE, 10) - .put(Enchantment.DURABILITY, 10) .put(Enchantment.PROTECTION_EXPLOSIONS, 5) - .put(Enchantment.PROTECTION_FALL, 5) .put(Enchantment.PROTECTION_FIRE, 5) .put(Enchantment.THORNS, 1) .build(); final static ImmutableMap BOOTS_MAP = new ImmutableMap.Builder() .putAll(ARMOR_MAP) - .put(Enchantment.WATER_WORKER, 5) - .put(Enchantment.DEPTH_STRIDER, 3) - .put(Enchantment.FROST_WALKER, 1) + .put(Enchantment.PROTECTION_FALL, 10) + .put(Enchantment.DEPTH_STRIDER, 3) + .put(Enchantment.FROST_WALKER, 1) .build(); final static ImmutableMap WEAPONS_MAP = new ImmutableMap.Builder() .put(Enchantment.DAMAGE_ALL, 15) - .put(Enchantment.DURABILITY, 10) .put(Enchantment.KNOCKBACK, 10) .put(Enchantment.MENDING, 5) .put(Enchantment.SWEEPING_EDGE, 5) @@ -53,7 +50,6 @@ public class EnchantmentMutation extends KitMutation { .build(); final static ImmutableMap TOOLS_MAP = new ImmutableMap.Builder() - .put(Enchantment.DURABILITY, 10) .put(Enchantment.DIG_SPEED, 10) .put(Enchantment.SILK_TOUCH, 5) .put(Enchantment.LOOT_BONUS_BLOCKS, 5) @@ -116,9 +112,9 @@ public class EnchantmentMutation extends KitMutation { byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments())); savedEnchantments.put(entity, byEntity); // Apply the new enchantments - int amountOfEnchants = LEVELS.choose(entropy); + int amountOfEnchants = LEVELS.choose(entropy()); for(int i = 0; i < amountOfEnchants; i++) { - item.addUnsafeEnchantment(chooser.choose(entropy), LEVELS.choose(entropy)); + item.addUnsafeEnchantment(chooser.choose(entropy()), LEVELS.choose(entropy())); } } @@ -129,7 +125,7 @@ public class EnchantmentMutation extends KitMutation { super.apply(player); player.getInventory().forEach(item -> { // Random number of enchantments on each item - int numberOfEnchants = LEVELS.choose(entropy); + int numberOfEnchants = LEVELS.choose(entropy()); for(int i = 0; i < numberOfEnchants; i++) { apply(item, player.getBukkit().getEquipment()); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java index 61d4922..026bdec 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java @@ -4,12 +4,11 @@ import com.google.common.collect.ImmutableMap; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Horse; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerSpawnEntityEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.HorseInventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SpawnEggMeta; @@ -17,23 +16,23 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; import tc.oc.commons.core.random.WeightedRandomChooser; -import tc.oc.pgm.events.MatchPlayerDeathEvent; -import tc.oc.pgm.events.PlayerChangePartyEvent; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.mutation.types.EntityMutation; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -public class EquestrianMutation extends KitMutation { +import static tc.oc.commons.core.util.Optionals.cast; + +public class EquestrianMutation extends EntityMutation { final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() .put(EntityType.HORSE, 100) - // FIXME: Saddle do not work on these horses + // FIXME: Saddles do not work on these horses //.put(EntityType.SKELETON_HORSE, 5) //.put(EntityType.ZOMBIE_HORSE, 5) //.put(EntityType.LLAMA, 1) @@ -49,18 +48,15 @@ public class EquestrianMutation extends KitMutation { final static WeightedRandomChooser TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP); final static WeightedRandomChooser ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP); - final Map horses; - public EquestrianMutation(Match match) { super(match, false); - this.horses = new WeakHashMap<>(); } @Override - public void disable() { - super.disable(); - match.participants().forEach(this::remove); - horses.clear(); + public void enable() { + super.enable(); + this.itemRemove.add(Material.LEATHER); + this.itemRemove.addAll(ARMOR_MAP.keySet()); } @Override @@ -72,7 +68,7 @@ public class EquestrianMutation extends KitMutation { if(!spawnable(location)) { ItemStack item = item(Material.MONSTER_EGG); SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); - egg.setSpawnedType(TYPES.choose(entropy)); + egg.setSpawnedType(TYPES.choose(entropy())); item.setItemMeta(egg); kits.add(new FreeItemKit(item)); } @@ -83,38 +79,31 @@ public class EquestrianMutation extends KitMutation { super.apply(player); Location location = player.getLocation(); if(spawnable(location)) { - setup(player, spawn(location, (Class) TYPES.choose(match.entropyForTick()).getEntityClass())); + spawn(location, (Class) TYPES.choose(entropy()).getEntityClass(), player); } } @Override - public void remove(MatchPlayer player) { - super.remove(player); - AbstractHorse horse = horses.remove(player); - if(horse != null) { - horse.ejectAll(); - horse.remove(); - } - } - - public void setup(MatchPlayer player, AbstractHorse horse) { - horses.put(player, horse); - horse.setAdult(); - horse.setJumpStrength(2 * entropy.randomDouble()); - horse.setDomestication(1); - horse.setMaxDomestication(1); - horse.setTamed(true); - horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0)); - horse.setOwner(player.getBukkit()); - horse.setPassenger(player.getBukkit()); - if(horse instanceof Horse) { - Horse horsey = (Horse) horse; - horsey.setStyle(entropy.randomElement(Horse.Style.values())); - horsey.setColor(entropy.randomElement(Horse.Color.values())); - HorseInventory inventory = horsey.getInventory(); - inventory.setSaddle(item(Material.SADDLE)); - inventory.setArmor(item(ARMOR.choose(entropy))); + public AbstractHorse register(AbstractHorse horse, @Nullable MatchPlayer owner) { + super.register(horse, owner); + if(owner != null) { + horse.setAdult(); + horse.setJumpStrength(2 * entropy().randomDouble()); + horse.setDomestication(1); + horse.setMaxDomestication(1); + horse.setTamed(true); + horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0)); + horse.setOwner(owner.getBukkit()); + horse.setPassenger(owner.getBukkit()); + cast(horse, Horse.class).ifPresent(horsey -> { + horsey.setStyle(entropy().randomElement(Horse.Style.values())); + horsey.setColor(entropy().randomElement(Horse.Color.values())); + HorseInventory inventory = horsey.getInventory(); + inventory.setSaddle(item(Material.SADDLE)); + inventory.setArmor(item(ARMOR.choose(entropy()))); + }); } + return horse; } public boolean spawnable(Location location) { @@ -129,22 +118,11 @@ public class EquestrianMutation extends KitMutation { } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onEntitySpawn(PlayerSpawnEntityEvent event) { - Entity entity = event.getEntity(); - if(TYPE_MAP.containsKey(entity.getType())) { - match.participant(event.getPlayer()) - .ifPresent(player -> setup(player, (AbstractHorse) event.getEntity())); + public void onEntityDamage(EntityDamageByEntityEvent event) { + if(playerByEntity(event.getEntity()).flatMap(MatchPlayer::competitor) + .equals(match().participant(event.getDamager()).flatMap(MatchPlayer::competitor))) { + event.setCancelled(true); } } - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPlayerDeath(MatchPlayerDeathEvent event) { - remove(event.getVictim()); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPartyChange(PlayerChangePartyEvent event) { - remove(event.getPlayer()); - } - } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java index d71e969..147f99d 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java @@ -3,13 +3,11 @@ package tc.oc.pgm.mutation.types.kit; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.ExplosionPrimeEvent; import org.bukkit.inventory.PlayerInventory; import tc.oc.commons.bukkit.item.ItemBuilder; -import tc.oc.commons.core.collection.WeakHashSet; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.ItemKit; @@ -30,26 +28,17 @@ public class ExplosiveMutation extends KitMutation { final static Range RADIUS = Range.openClosed(0, 4); - final WeakHashSet tracked; - public ExplosiveMutation(Match match) { super(match, false); - this.tracked = new WeakHashSet<>(); this.rewards.add(new KillReward(TNT)); this.rewards.add(new KillReward(ARROWS)); } - @Override - public void disable() { - super.disable(); - tracked.clear(); - } - @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); PlayerInventory inv = player.getInventory(); - if(random.nextBoolean()) { // tnt and lighter kit + if(random().nextBoolean()) { // tnt and lighter kit if(!inv.contains(Material.TNT)) kits.add(TNT); if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER); } else { // fire bow and arrows kit @@ -62,9 +51,15 @@ public class ExplosiveMutation extends KitMutation { } } + @Override + public void remove(MatchPlayer player) { + player.getInventory().all(Material.BOW).values().forEach(bow -> FIRE_BOW.item().getEnchantments().keySet().forEach(enchantment -> bow.removeEnchantment(enchantment))); + super.remove(player); + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onExplosionPrime(ExplosionPrimeEvent event) { - event.setRadius(event.getRadius() + entropy.randomInt(RADIUS)); + event.setRadius(event.getRadius() + entropy().randomInt(RADIUS)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java index e5e5196..f136c81 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java @@ -1,7 +1,6 @@ package tc.oc.pgm.mutation.types.kit; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import tc.oc.pgm.gamerules.GameRulesMatchModule; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; @@ -26,13 +25,13 @@ public class HardcoreMutation extends KitMutation { } public GameRulesMatchModule rules() { - return match.module(GameRulesMatchModule.class).get(); + return match().module(GameRulesMatchModule.class).get(); } @Override public void enable() { super.enable(); - previous = world.getGameRuleValue(KEY); + previous = world().getGameRuleValue(KEY); rules().gameRules().put(KEY, "false"); } @@ -40,7 +39,7 @@ public class HardcoreMutation extends KitMutation { public void disable() { rules().gameRules().remove(KEY); if(previous != null) { - world.setGameRuleValue(KEY, previous); + world().setGameRuleValue(KEY, previous); } super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java index afcdfba..72e1343 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java @@ -1,7 +1,6 @@ package tc.oc.pgm.mutation.types.kit; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.HealthKit; diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java index 9855a17..32c9e24 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SpawnEggMeta; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; @@ -12,11 +13,11 @@ import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.mutation.types.EntityMutation; import java.util.List; -public class MobsMutation extends KitMutation { +public class MobsMutation extends EntityMutation { final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() .put(EntityType.ZOMBIE, 50) @@ -37,17 +38,17 @@ public class MobsMutation extends KitMutation { final static Range AMOUNT = Range.closed(1, 3); public MobsMutation(Match match) { - super(match, true); + super(match, false); } @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); - int eggs = entropy.randomInt(AMOUNT); + int eggs = entropy().randomInt(AMOUNT); for(int i = 0; i < eggs; i++) { - ItemStack item = item(Material.MONSTER_EGG, entropy.randomInt(AMOUNT)); + ItemStack item = item(Material.MONSTER_EGG, entropy().randomInt(AMOUNT)); SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); - egg.setSpawnedType(TYPES.choose(entropy)); + egg.setSpawnedType(TYPES.choose(entropy())); item.setItemMeta(egg); kits.add(new FreeItemKit(item)); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java index eb3363b..6f1a095 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java @@ -22,7 +22,7 @@ public abstract class NoFallMutation extends KitMutation { } public DisableDamageMatchModule damage() { - return match.module(DisableDamageMatchModule.class).get(); + return match().module(DisableDamageMatchModule.class).get(); } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java index 6a51913..87f7a4e 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java @@ -68,13 +68,13 @@ public class PotionMutation extends KitMutation { @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); - int numberOfPotions = entropy.randomInt(AMOUNT_RANGE); + int numberOfPotions = entropy().randomInt(AMOUNT_RANGE); for(int i = 0; i < numberOfPotions; i++) { WeightedRandomChooser type; WeightedRandomChooser material; Range range; // Determine whether the potion will be "good" or "bad" - if(random.nextBoolean()) { + if(random().nextBoolean()) { type = BAD; material = BAD_BOTTLE; range = BAD_DURATION_RANGE; @@ -84,10 +84,10 @@ public class PotionMutation extends KitMutation { range = GOOD_DURATION_RANGE; } // Choose all the random attributes - PotionEffectType effect = type.choose(entropy); - Material bottle = material.choose(entropy); - int duration = 20 * entropy.randomInt(range); - int amplifier = entropy.randomInt(AMPLIFIER_RANGE); + PotionEffectType effect = type.choose(entropy()); + Material bottle = material.choose(entropy()); + int duration = 20 * entropy().randomInt(range); + int amplifier = entropy().randomInt(AMPLIFIER_RANGE); // Apply the attributes to the item stack ItemStack potion = item(bottle); PotionMeta meta = (PotionMeta) potion.getItemMeta(); diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java index 60cf9cc..8fa7cdc 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java @@ -3,20 +3,17 @@ package tc.oc.pgm.mutation.types.kit; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Arrow; -import org.bukkit.entity.Entity; -import org.bukkit.entity.TippedArrow; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityShootBowEvent; -import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.inventory.Inventory; -import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; import tc.oc.commons.core.random.WeightedRandomChooser; -import tc.oc.pgm.PGM; +import tc.oc.commons.core.util.Optionals; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.ItemKit; @@ -26,6 +23,7 @@ import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.mutation.types.KitMutation; import java.util.List; +import java.util.Optional; public class ProjectileMutation extends KitMutation { @@ -39,8 +37,6 @@ public class ProjectileMutation extends KitMutation { final static ItemKit BOW = new FreeItemKit(item(Material.BOW)); final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16)); - final static String KEY = "is_modified_arrow"; - public ProjectileMutation(Match match) { super(match, false); this.rewards.add(new KillReward(ARROWS)); @@ -50,7 +46,7 @@ public class ProjectileMutation extends KitMutation { public void apply(MatchPlayer player) { super.apply(player); Inventory inventory = player.getInventory(); - inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy), entropy.randomInt(ENCHANT_RANGE))); + inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy()), entropy().randomInt(ENCHANT_RANGE))); } @Override @@ -62,23 +58,10 @@ public class ProjectileMutation extends KitMutation { } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onBowShoot(EntityShootBowEvent event) { - Entity projectile = event.getProjectile(); - if(projectile instanceof Arrow && (!projectile.hasMetadata(KEY) || !projectile.getMetadata(KEY, PGM.get()).asBoolean())) { - Arrow arrow = (Arrow) projectile; - TippedArrow tipped = world.spawn(projectile.getLocation(), TippedArrow.class); - tipped.setMetadata(KEY, new FixedMetadataValue(PGM.get(), true)); - tipped.setCritical(arrow.isCritical()); - tipped.setKnockbackStrength(arrow.getKnockbackStrength()); - tipped.setDamage(arrow.getDamage()); - tipped.setShooter(arrow.getShooter()); - tipped.setVelocity(projectile.getVelocity()); - tipped.setPickupRule(Arrow.PickupRule.DISALLOWED); - tipped.addCustomEffect(new PotionEffect(POTIONS.choose(entropy), 20 * entropy.randomInt(DURATION_RANGE), entropy.randomInt(AMPLIFIER_RANGE)), true); - arrow.remove(); - event.setCancelled(true); - match.callEvent(new ProjectileLaunchEvent(tipped)); - } + public void onProjectileHit(ProjectileHitEvent event) { + Optionals.cast(Optional.ofNullable(event.getHitEntity()), LivingEntity.class) + .filter(entity -> Optional.ofNullable(event.getEntity().getShooter()).filter(shooter -> shooter instanceof Player).isPresent()) + .ifPresent(entity -> entity.addPotionEffect(new PotionEffect(POTIONS.choose(entropy()), 20 * entropy().randomInt(DURATION_RANGE), entropy().randomInt(AMPLIFIER_RANGE)), true)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java index 7062f61..1a80629 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java @@ -11,7 +11,7 @@ import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.mutation.types.MutationModule; import tc.oc.pgm.teams.TeamMatchModule; -public class BlitzMutation extends MutationModule { +public class BlitzMutation extends MutationModule.Impl { final static Range LIVES = Range.closed(1, 3); final static Fraction TEAM_CHANCE = Fraction.ONE_QUARTER; @@ -23,22 +23,22 @@ public class BlitzMutation extends MutationModule { @Override public void enable() { super.enable(); - int lives = match.entropyForTick().randomInt(LIVES); + int lives = entropy().randomInt(LIVES); Lives.Type type; - if(match.module(TeamMatchModule.class).isPresent() && RandomUtils.nextBoolean(random, TEAM_CHANCE)) { + if(match().module(TeamMatchModule.class).isPresent() && RandomUtils.nextBoolean(random(), TEAM_CHANCE)) { type = Lives.Type.TEAM; - lives *= match.module(TeamMatchModule.class).get().getFullestTeam().getSize(); + lives *= match().module(TeamMatchModule.class).get().getFullestTeam().getSize(); } else { type = Lives.Type.INDIVIDUAL; } - match.module(BlitzMatchModuleImpl.class).get().activate(BlitzProperties.create(match, lives, type)); + match().module(BlitzMatchModuleImpl.class).get().activate(BlitzProperties.create(match(), lives, type)); } @Override public void disable() { - match.getScheduler(MatchScope.LOADED).createTask(() -> { - if(!match.isFinished()) { - match.module(BlitzMatchModuleImpl.class).get().deactivate(); + match().getScheduler(MatchScope.LOADED).createTask(() -> { + if(!match().isFinished()) { + match().module(BlitzMatchModuleImpl.class).get().deactivate(); } }); super.disable(); diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java index 66988d2..94af87d 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java @@ -7,7 +7,7 @@ import tc.oc.pgm.match.Match; import tc.oc.pgm.mutation.types.MutationModule; import tc.oc.pgm.rage.RageMatchModule; -public class RageMutation extends MutationModule { +public class RageMutation extends MutationModule.Impl { RageMatchModule rage; @@ -26,4 +26,5 @@ public class RageMutation extends MutationModule { super.disable(); rage = null; } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java index 7733a81..c835cc9 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java @@ -21,6 +21,7 @@ import tc.oc.commons.core.random.WeightedRandomChooser; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.EntityMutation; import tc.oc.pgm.mutation.types.kit.EnchantmentMutation; import tc.oc.pgm.mutation.types.TargetMutation; import tc.oc.pgm.points.PointProviderAttributes; @@ -28,19 +29,17 @@ import tc.oc.pgm.points.RandomPointProvider; import tc.oc.pgm.points.RegionPointProvider; import tc.oc.pgm.regions.CuboidRegion; +import javax.annotation.Nullable; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.WeakHashMap; import static tc.oc.commons.core.random.RandomUtils.nextBoolean; -public class ApocalypseMutation extends TargetMutation { +public class ApocalypseMutation extends EntityMutation implements TargetMutation { final static ImmutableMap AMOUNT_MAP = new ImmutableMap.Builder() .put(3, 25) @@ -104,6 +103,7 @@ public class ApocalypseMutation extends TargetMutation { final static WeightedRandomChooser PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP); final static WeightedRandomChooser CUBE = new ImmutableWeightedRandomChooser<>(CUBE_MAP); + final static Range FREQUENCY = Range.closed(5, 30); // Seconds between entity spawns final static int DISTANCE = 15; // Max distance entities spawn from players final static int PARTICIPANT_ENTITIES = 25; // Max entities on the field per participant final static int MAX_ENTITIES = 500; // Max total entities on the field @@ -111,33 +111,34 @@ public class ApocalypseMutation extends TargetMutation { final static Fraction SPECIAL_CHANCE = Fraction.ONE_FIFTH; // Chance of a special attribute occuring in an entity final static int SPECIAL_MULTIPLIER = 3; // Multiplier for special attributes - final WeakHashMap entities; - long time; + long time; // world time + Instant next; // next time to spawn entities + final PointProviderAttributes attributes; // attributes to choosing random points public ApocalypseMutation(Match match) { - super(match, Duration.ofSeconds(20)); - this.entities = new WeakHashMap<>(); + super(match, false); + this.attributes = new PointProviderAttributes(null, null, true, false); } /** * Get the maximum amount of entities that can be spawned. */ public int entitiesMax() { - return Math.min((int) match.participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES); + return Math.min((int) match().participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES); } /** * Get the number of available slots are left for additional entities to spawn. */ public int entitiesLeft() { - return entitiesMax() - world.getLivingEntities().size() + (int) match.participants().count(); + return entitiesMax() - world().getLivingEntities().size() + (int) match().participants().count(); } /** * Generate a random spawn point given two locations. */ public Optional location(Location start, Location end) { - return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), new PointProviderAttributes()))).getPoint(match, null)); + return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), attributes))).getPoint(match(), null)); } /** @@ -147,15 +148,15 @@ public class ApocalypseMutation extends TargetMutation { */ public void spawn(Location location, boolean ground) { int slots = entitiesLeft(); - int queued = AMOUNT.choose(entropy); + int queued = AMOUNT.choose(entropy()); // Remove any entities that may be over the max limit despawn(queued - slots); // Determine whether the entities should be airborn - int stack = STACK.choose(entropy); - boolean air = !ground || nextBoolean(random, SPECIAL_CHANCE); + int stack = STACK.choose(entropy()); + boolean air = !ground || nextBoolean(random(), SPECIAL_CHANCE); if(air) { - stack += (stack == 1 && random.nextBoolean() ? 1 : 0); - location.add(0, entropy.randomInt(AIR_OFFSET), 0); + stack += (stack == 1 && random().nextBoolean() ? 1 : 0); + location.add(0, entropy().randomInt(AIR_OFFSET), 0); } // Select the random entity chooser based on ground, air, and stacked boolean stacked = stack > 1; @@ -163,7 +164,7 @@ public class ApocalypseMutation extends TargetMutation { if(air) { if(stacked) { if(ground) { - chooser = nextBoolean(random, SPECIAL_CHANCE) ? CUBE : FLYABLE; + chooser = nextBoolean(random(), SPECIAL_CHANCE) ? CUBE : FLYABLE; } else { chooser = FLYABLE; } @@ -174,7 +175,7 @@ public class ApocalypseMutation extends TargetMutation { if(stacked) { chooser = GROUND; } else { - chooser = random.nextBoolean() ? GROUND : RANGED; + chooser = random().nextBoolean() ? GROUND : RANGED; } } // Select the specific entity types for the spawn, @@ -182,13 +183,13 @@ public class ApocalypseMutation extends TargetMutation { // but may have variations (like armor) between them. List types = new ArrayList<>(); for(int i = 0; i < stack; i++) { - types.add((i == 0 ? chooser : PASSENGER).choose(entropy)); + types.add((i == 0 ? chooser : PASSENGER).choose(entropy())); } // Spawn the mobs and stack them if required for(int i = 0; i < queued; i++) { Entity last = null; for(EntityType type : types) { - Entity entity = spawn(location, type); + Entity entity = spawn(location, (Class) type.getEntityClass()); if(last != null) { last.setPassenger(entity); } @@ -198,20 +199,14 @@ public class ApocalypseMutation extends TargetMutation { } - /** - * Spawn an individual entitiy at a location given an entity type. - * @param location the location to spawn at. - * @param type the type of entity. - * @return the constructed entity. - */ - public LivingEntity spawn(Location location, EntityType type) { - EnchantmentMutation enchant = new EnchantmentMutation(match); - LivingEntity entity = (LivingEntity) spawn(location, type.getEntityClass()); + @Override + public LivingEntity spawn(Location location, Class entityClass, @Nullable MatchPlayer owner) { + LivingEntity entity = super.spawn(location, entityClass, owner); + EnchantmentMutation enchant = new EnchantmentMutation(match()); EntityEquipment equipment = entity.getEquipment(); entity.setVelocity(Vector.getRandom()); - entity.setAbsorption(5); ItemStack held = null; - switch(type) { + switch(entity.getType()) { case SKELETON: case WITHER_SKELETON: case STRAY: @@ -221,7 +216,7 @@ public class ApocalypseMutation extends TargetMutation { case ZOMBIE_VILLAGER: case HUSK: Zombie zombie = (Zombie) entity; - zombie.setBaby(nextBoolean(random, SPECIAL_CHANCE)); + zombie.setBaby(nextBoolean(random(), SPECIAL_CHANCE)); break; case PIG_ZOMBIE: PigZombie pigZombie = (PigZombie) entity; @@ -231,8 +226,8 @@ public class ApocalypseMutation extends TargetMutation { break; case CREEPER: Creeper creeper = (Creeper) entity; - creeper.setPowered(nextBoolean(random, SPECIAL_CHANCE)); - world.strikeLightningEffect(location); + creeper.setPowered(nextBoolean(random(), SPECIAL_CHANCE)); + world().strikeLightningEffect(location); break; case PRIMED_TNT: TNTPrimed tnt = (TNTPrimed) entity; @@ -244,14 +239,13 @@ public class ApocalypseMutation extends TargetMutation { slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER); break; case SKELETON_HORSE: - world.strikeLightning(location); + world().strikeLightning(location); break; } - if(held != null && random.nextBoolean()) { + if(held != null && random().nextBoolean()) { enchant.apply(held, equipment); equipment.setItemInMainHand(held); } - entities.put(entity, Instant.now()); return entity; } @@ -260,17 +254,12 @@ public class ApocalypseMutation extends TargetMutation { * to make room for new entities. * @param amount the amount of entities to despawn. */ - public void despawn(int amount) { - entities.entrySet() - .stream() - .sorted(Map.Entry.comparingByValue(Comparator.comparing(Instant::toEpochMilli))) - .limit(Math.max(0, amount)) - .map(Map.Entry::getKey) - .forEach(Entity::remove); + public void despawn(long amount) { + entitiesByTime().limit(Math.max(0, amount)).forEachOrdered(this::despawn); } @Override - public void execute(List players) { + public void target(List players) { // At least one player is required to spawn mobs if(players.size() >= 1) { Location start, end; @@ -284,7 +273,7 @@ public class ApocalypseMutation extends TargetMutation { if(location.isPresent()) { // if the location is safe (on ground) spawn(location.get(), true); } else { // if the location was not safe, generate a simple midpoint location - spawn(start.position().midpoint(end.position()).toLocation(world), false); + spawn(start.position().midpoint(end.position()).toLocation(world()), false); } } } @@ -294,21 +283,38 @@ public class ApocalypseMutation extends TargetMutation { return 2; // Always require 2 targets to generate a spawn location between them } + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return Duration.ofSeconds(entropy().randomInt(FREQUENCY)); + } + @Override public void enable() { super.enable(); - time = world.getTime(); + TargetMutation.super.enable(); + time = world().getTime(); } @Repeatable public void tick() { - world.setTime(16000); // Night time to prevent flaming entities + TargetMutation.super.tick(); + world().setTime(16000); // Night time to prevent flaming entities } @Override public void disable() { - world.setTime(time); - despawn(entities.size()); + world().setTime(time); + despawn(entities().count()); super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java index 6cd8b40..7c095ad 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java @@ -4,63 +4,85 @@ import com.google.common.collect.Range; import org.bukkit.Location; import org.bukkit.entity.TNTPrimed; import org.bukkit.util.Vector; -import tc.oc.commons.core.collection.WeakHashSet; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.EntityMutation; import tc.oc.pgm.mutation.types.TargetMutation; import java.time.Duration; +import java.time.Instant; import java.util.List; -public class BomberMutation extends TargetMutation { +public class BomberMutation extends EntityMutation implements TargetMutation { final static Duration FREQUENCY = Duration.ofSeconds(30); - final static Range TARGETS = Range.closed(1, 5); + final static Range TARGETS = Range.closed(2, 5); final static Range HEIGHT = Range.closed(30, 60); final static Range TICKS = Range.closed(10, 30); - final WeakHashSet falling; + Instant next; public BomberMutation(Match match) { - super(match, FREQUENCY); - this.falling = new WeakHashSet<>(); + super(match, false); } @Override - public void execute(List players) { + public void target(List players) { players.forEach(player -> { - int bombs = entropy.randomInt(TARGETS); - int height = entropy.randomInt(HEIGHT); + int bombs = entropy().randomInt(TARGETS); + int height = entropy().randomInt(HEIGHT); Location location = player.getLocation().clone().add(0, height, 0); for(int i = 0; i < bombs; i++) { - TNTPrimed tnt = world.spawn(location, TNTPrimed.class); + TNTPrimed tnt = spawn(location, TNTPrimed.class); tnt.setGlowing(true); tnt.setIsIncendiary(false); - tnt.setFuseTicks(Integer.MAX_VALUE); + tnt.setFuseTicks(200); tnt.setVelocity( new Vector( - (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble(), - -entropy.randomDouble(), - (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble() + (random().nextBoolean() ? .5 : -.5) * entropy().randomDouble(), + -entropy().randomDouble(), + (random().nextBoolean() ? .5 : -.5) * entropy().randomDouble() ) ); - falling.add(tnt); } }); } @Override public int targets() { - return match.entropyForTick().randomInt(TARGETS); + return match().entropyForTick().randomInt(TARGETS); } - @Repeatable + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return FREQUENCY; + } + + @Override + public void remove(TNTPrimed entity) { + entity.setFuseTicks(entropy().randomInt(TICKS)); + } + + @Override + public void enable() { + super.enable(); + TargetMutation.super.enable(); + } + + @Override public void tick() { - falling.stream() - .filter(TNTPrimed::isOnGround) - .forEach(tnt -> tnt.setFuseTicks(entropy.randomInt(TICKS))); - falling.removeIf(TNTPrimed::isOnGround); + TargetMutation.super.tick(); + entities().filter(TNTPrimed::isOnGround).forEach(this::despawn); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java index 583d931..42b1f89 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java @@ -2,7 +2,6 @@ package tc.oc.pgm.mutation.types.targetable; import com.google.common.collect.Range; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.util.Vector; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; @@ -11,32 +10,31 @@ import tc.oc.pgm.mutation.types.TargetMutation; import java.time.Duration; import java.util.List; -public class LightningMutation extends TargetMutation { +public class LightningMutation extends TargetMutation.Impl { final static Duration FREQUENCY = Duration.ofSeconds(30); - final static Range TARGETS = Range.closed(1, 5); - final static Range STRIKES = Range.closed(0, 3); + final static Range TARGETS = Range.closed(2, 5); + final static Range STRIKES = Range.closed(1, 3); public LightningMutation(Match match) { super(match, FREQUENCY); } @Override - public void execute(List players) { + public void target(List players) { players.forEach(player -> { - World world = match.getWorld(); Location location = player.getLocation(); - world.strikeLightning(location.clone().add(Vector.getRandom())); - int strikes = match.entropyForTick().randomInt(STRIKES); + world().strikeLightning(location.clone().add(Vector.getRandom())); + int strikes = entropy().randomInt(STRIKES); for(int i = 0; i < strikes; i++) { - world.strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2)))); + world().strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2)))); } }); } @Override public int targets() { - return match.entropyForTick().randomInt(TARGETS); + return match().entropyForTick().randomInt(TARGETS); } } From 3e627fabfb92a52d6e9aec7666689b0fb23200bd Mon Sep 17 00:00:00 2001 From: BuildTools <18whitechristop@gmail.com> Date: Mon, 8 May 2017 02:42:12 -0700 Subject: [PATCH 013/252] Add tokens and add shiny's gui library --- .../java/tc/oc/api/docs/virtual/UserDoc.java | 3 +- .../oc/api/users/CreditMaptokensRequest.java | 9 + .../users/CreditMutationtokensRequest.java | 9 + .../java/tc/oc/api/users/NullUserService.java | 10 + .../java/tc/oc/api/users/UserService.java | 12 + .../minecraft/users/LocalUserDocument.java | 10 + .../api/minecraft/users/LocalUserService.java | 42 +- .../java/tc/oc/api/ocn/OCNUserService.java | 22 +- .../commons/bukkit/CommonsBukkitManifest.java | 2 + .../bukkit/event/InterfaceOpenEvent.java | 36 ++ .../tc/oc/commons/bukkit/gui/Interface.java | 75 +++ .../commons/bukkit/gui/InterfaceHolder.java | 12 + .../commons/bukkit/gui/InterfaceManager.java | 68 +++ .../bukkit/gui/SimpleInterfaceHolder.java | 32 ++ .../oc/commons/bukkit/gui/buttons/Button.java | 62 +++ .../bukkit/gui/buttons/empty/EmptyButton.java | 13 + .../gui/buttons/lastPage/LastPageButton.java | 72 +++ .../gui/buttons/nextPage/NextPageButton.java | 57 +++ .../gui/buttons/toggle/ToggleButton.java | 27 ++ .../toggle/type/BooleanToggleType.java | 4 + .../buttons/toggle/type/EnumToggleType.java | 11 + .../gui/buttons/toggle/type/ToggleType.java | 4 + .../bukkit/gui/interfaces/ChestInterface.java | 63 +++ .../interfaces/ChestOptionsPageInterface.java | 146 ++++++ .../gui/interfaces/ChestPageInterface.java | 172 +++++++ .../gui/interfaces/HopperInterface.java | 54 +++ .../gui/interfaces/InterfaceInventory.java | 25 + .../gui/interfaces/SinglePageInterface.java | 201 ++++++++ .../render/ChestRenderInterface.java | 111 +++++ .../gui/interfaces/render/Coordinate.java | 29 ++ .../render/text/GridTextRenderInterface.java | 431 ++++++++++++++++++ .../commons/bukkit/tokens/TokenCommands.java | 94 ++++ .../commons/bukkit/tokens/TokenManifest.java | 14 + .../oc/commons/bukkit/tokens/TokenUtil.java | 40 ++ .../tc/oc/commons/bukkit/util/Constants.java | 23 + .../oc/commons/bukkit/util/ItemCreator.java | 183 ++++++++ .../oc/commons/bukkit/util/ObjectUtils.java | 20 + Commons/bukkit/src/main/resources/plugin.yml | 3 + .../i18n/templates/commons/Commons.properties | 16 + .../i18n/templates/pgm/PGMMessages.properties | 5 + .../main/i18n/templates/pgm/PGMUI.properties | 2 + PGM/pom.xml | 1 - PGM/src/main/java/tc/oc/pgm/Config.java | 9 + PGM/src/main/java/tc/oc/pgm/PGM.java | 11 + PGM/src/main/java/tc/oc/pgm/PGMManifest.java | 2 + .../java/tc/oc/pgm/PGMModulesManifest.java | 2 + .../java/tc/oc/pgm/commands/PollCommands.java | 66 ++- .../oc/pgm/listeners/InterfaceListener.java | 86 ++++ .../java/tc/oc/pgm/match/MatchManager.java | 10 + .../mutation/command/MutationCommands.java | 11 + .../tc/oc/pgm/pollablemaps/PollableMaps.java | 60 +++ PGM/src/main/java/tc/oc/pgm/polls/Poll.java | 19 +- .../java/tc/oc/pgm/polls/PollMutation.java | 44 ++ .../java/tc/oc/pgm/polls/PollNextMap.java | 12 +- .../java/tc/oc/pgm/tokens/TokenListener.java | 89 ++++ .../java/tc/oc/pgm/tokens/TokenManifest.java | 13 + .../tc/oc/pgm/tokens/gui/MainTokenButton.java | 33 ++ .../tc/oc/pgm/tokens/gui/MainTokenMenu.java | 74 +++ .../tokens/gui/MutationConfirmInterface.java | 77 ++++ .../tokens/gui/MutationTokenInterface.java | 124 +++++ PGM/src/main/resources/config.yml | 4 + PGM/src/main/resources/plugin.yml | 5 + .../tc/oc/pgm/filter/ItemMatcherTest.java | 160 ------- 63 files changed, 2938 insertions(+), 198 deletions(-) create mode 100644 API/api/src/main/java/tc/oc/api/users/CreditMaptokensRequest.java create mode 100644 API/api/src/main/java/tc/oc/api/users/CreditMutationtokensRequest.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/event/InterfaceOpenEvent.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/Interface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/InterfaceHolder.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/InterfaceManager.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/SimpleInterfaceHolder.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/Button.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/empty/EmptyButton.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/lastPage/LastPageButton.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/nextPage/NextPageButton.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/toggle/ToggleButton.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/toggle/type/BooleanToggleType.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/toggle/type/EnumToggleType.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/buttons/toggle/type/ToggleType.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/ChestInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/ChestOptionsPageInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/ChestPageInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/HopperInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/InterfaceInventory.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/SinglePageInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/render/ChestRenderInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/render/Coordinate.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/interfaces/render/text/GridTextRenderInterface.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/tokens/TokenCommands.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/tokens/TokenManifest.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/tokens/TokenUtil.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/util/Constants.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/util/ItemCreator.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/util/ObjectUtils.java create mode 100644 PGM/src/main/java/tc/oc/pgm/listeners/InterfaceListener.java create mode 100644 PGM/src/main/java/tc/oc/pgm/pollablemaps/PollableMaps.java create mode 100644 PGM/src/main/java/tc/oc/pgm/polls/PollMutation.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/TokenListener.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/TokenManifest.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/gui/MainTokenButton.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/gui/MainTokenMenu.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/gui/MutationConfirmInterface.java create mode 100644 PGM/src/main/java/tc/oc/pgm/tokens/gui/MutationTokenInterface.java delete mode 100644 PGM/src/test/java/tc/oc/pgm/filter/ItemMatcherTest.java diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java index f9d5ffb..c3c24ab 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java @@ -1,6 +1,5 @@ package tc.oc.api.docs.virtual; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -82,6 +81,8 @@ public interface UserDoc { @Serialize interface Login extends Identity, Locale, Trophies, License.Complete { int raindrops(); + int maptokens(); + int mutationtokens(); String mc_last_sign_in_ip(); @Nullable Date trial_expires_at(); Map> mc_permissions_by_realm(); diff --git a/API/api/src/main/java/tc/oc/api/users/CreditMaptokensRequest.java b/API/api/src/main/java/tc/oc/api/users/CreditMaptokensRequest.java new file mode 100644 index 0000000..3defcfc --- /dev/null +++ b/API/api/src/main/java/tc/oc/api/users/CreditMaptokensRequest.java @@ -0,0 +1,9 @@ +package tc.oc.api.users; + +import tc.oc.api.annotations.Serialize; +import tc.oc.api.docs.virtual.Document; + +@Serialize +public interface CreditMaptokensRequest extends Document { + int maptokens(); +} diff --git a/API/api/src/main/java/tc/oc/api/users/CreditMutationtokensRequest.java b/API/api/src/main/java/tc/oc/api/users/CreditMutationtokensRequest.java new file mode 100644 index 0000000..4eeefed --- /dev/null +++ b/API/api/src/main/java/tc/oc/api/users/CreditMutationtokensRequest.java @@ -0,0 +1,9 @@ +package tc.oc.api.users; + +import tc.oc.api.annotations.Serialize; +import tc.oc.api.docs.virtual.Document; + +@Serialize +public interface CreditMutationtokensRequest extends Document { + int mutationtokens(); +} diff --git a/API/api/src/main/java/tc/oc/api/users/NullUserService.java b/API/api/src/main/java/tc/oc/api/users/NullUserService.java index f25e10c..7104207 100644 --- a/API/api/src/main/java/tc/oc/api/users/NullUserService.java +++ b/API/api/src/main/java/tc/oc/api/users/NullUserService.java @@ -40,6 +40,16 @@ public class NullUserService extends NullModelService imp return Futures.immediateFailedFuture(new NotFound()); } + @Override + public ListenableFuture creditMaptokens(UserId userId, CreditMaptokensRequest request) { + return Futures.immediateFuture(UserUpdateResponse.FAILURE); + } + + @Override + public ListenableFuture creditMutationtokens(UserId userId, CreditMutationtokensRequest request) { + return Futures.immediateFuture(UserUpdateResponse.FAILURE); + } + @Override public ListenableFuture update(UserId userId, T update) { return Futures.immediateFailedFuture(new NotFound()); diff --git a/API/api/src/main/java/tc/oc/api/users/UserService.java b/API/api/src/main/java/tc/oc/api/users/UserService.java index 3d50ac2..2c30e98 100644 --- a/API/api/src/main/java/tc/oc/api/users/UserService.java +++ b/API/api/src/main/java/tc/oc/api/users/UserService.java @@ -27,6 +27,18 @@ public interface UserService extends ModelService { ListenableFuture purchaseGizmo(UserId userId, PurchaseGizmoRequest request); + default ListenableFuture creditMaptokens(UserId userId, int maptokens) { + return creditMaptokens(userId, () -> maptokens); + } + + ListenableFuture creditMaptokens(UserId userId, CreditMaptokensRequest request); + + default ListenableFuture creditMutationtokens(UserId userId, int mutationtokens) { + return creditMutationtokens(userId, () -> mutationtokens); + } + + ListenableFuture creditMutationtokens(UserId userId, CreditMutationtokensRequest request); + ListenableFuture update(UserId userId, T update); ListenableFuture changeSetting(UserId userId, ChangeSettingRequest request); diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java index 89e0fda..47666d0 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java @@ -80,6 +80,16 @@ public class LocalUserDocument extends SimplePlayerId implements User { return 0; } + @Override + public int maptokens() { + return 0; + } + + @Override + public int mutationtokens() { + return 0; + } + @Override public String mc_last_sign_in_ip() { return ip; diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserService.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserService.java index 681af50..eeb90a5 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserService.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserService.java @@ -18,17 +18,7 @@ import tc.oc.api.docs.virtual.UserDoc; import tc.oc.api.exceptions.NotFound; import tc.oc.api.minecraft.sessions.LocalSessionFactory; import tc.oc.api.model.NullModelService; -import tc.oc.api.users.ChangeClassRequest; -import tc.oc.api.users.ChangeSettingRequest; -import tc.oc.api.users.CreditRaindropsRequest; -import tc.oc.api.users.LoginRequest; -import tc.oc.api.users.LoginResponse; -import tc.oc.api.users.LogoutRequest; -import tc.oc.api.users.PurchaseGizmoRequest; -import tc.oc.api.users.UserSearchRequest; -import tc.oc.api.users.UserSearchResponse; -import tc.oc.api.users.UserService; -import tc.oc.api.users.UserUpdateResponse; +import tc.oc.api.users.*; import tc.oc.commons.core.concurrent.FutureUtils; import tc.oc.minecraft.api.user.UserFinder; @@ -138,6 +128,36 @@ class LocalUserService extends NullModelService implement return find(userId); } + @Override + public ListenableFuture creditMaptokens(UserId userId, CreditMaptokensRequest request) { + return FutureUtils.mapSync(find(userId), user -> new UserUpdateResponse() { + @Override + public boolean success() { + return true; + } + + @Override + public User user() { + return user; + } + }); + } + + @Override + public ListenableFuture creditMutationtokens(UserId userId, CreditMutationtokensRequest request) { + return FutureUtils.mapSync(find(userId), user -> new UserUpdateResponse() { + @Override + public boolean success() { + return true; + } + + @Override + public User user() { + return user; + } + }); + } + @Override public ListenableFuture update(UserId userId, T update) { return find(userId); diff --git a/API/ocn/src/main/java/tc/oc/api/ocn/OCNUserService.java b/API/ocn/src/main/java/tc/oc/api/ocn/OCNUserService.java index ea1b115..e556754 100644 --- a/API/ocn/src/main/java/tc/oc/api/ocn/OCNUserService.java +++ b/API/ocn/src/main/java/tc/oc/api/ocn/OCNUserService.java @@ -15,17 +15,7 @@ import tc.oc.api.message.types.PlayerTeleportRequest; import tc.oc.api.minecraft.users.UserStore; import tc.oc.api.model.HttpModelService; import tc.oc.api.queue.Exchange; -import tc.oc.api.users.ChangeClassRequest; -import tc.oc.api.users.ChangeSettingRequest; -import tc.oc.api.users.CreditRaindropsRequest; -import tc.oc.api.users.LoginRequest; -import tc.oc.api.users.LoginResponse; -import tc.oc.api.users.LogoutRequest; -import tc.oc.api.users.PurchaseGizmoRequest; -import tc.oc.api.users.UserSearchRequest; -import tc.oc.api.users.UserSearchResponse; -import tc.oc.api.users.UserService; -import tc.oc.api.users.UserUpdateResponse; +import tc.oc.api.users.*; import tc.oc.commons.core.concurrent.FutureUtils; import tc.oc.minecraft.api.entity.Player; @@ -91,6 +81,16 @@ class OCNUserService extends HttpModelService implements return handleUpdate(client().post(memberUri(userId, "purchase_gizmo"), request, User.class, HttpOption.INFINITE_RETRY)); } + @Override + public ListenableFuture creditMaptokens(UserId userId, CreditMaptokensRequest request) { + return handleUserUpdate(client().post(memberUri(userId, "credit_maptokens"), request, UserUpdateResponse.class, HttpOption.INFINITE_RETRY)); + } + + @Override + public ListenableFuture creditMutationtokens(UserId userId, CreditMutationtokensRequest request) { + return handleUserUpdate(client().post(memberUri(userId, "credit_mutationtokens"), request, UserUpdateResponse.class, HttpOption.INFINITE_RETRY)); + } + @Override public ListenableFuture update(UserId userId, T update) { return update(userId.player_id(), update); diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java index 52db0d5..20ab3ec 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java @@ -80,6 +80,7 @@ import tc.oc.commons.bukkit.ticket.TicketBooth; import tc.oc.commons.bukkit.ticket.TicketCommands; import tc.oc.commons.bukkit.ticket.TicketDisplay; import tc.oc.commons.bukkit.ticket.TicketListener; +import tc.oc.commons.bukkit.tokens.TokenManifest; import tc.oc.commons.bukkit.trophies.TrophyCase; import tc.oc.commons.bukkit.trophies.TrophyCommands; import tc.oc.commons.bukkit.users.JoinMessageManifest; @@ -110,6 +111,7 @@ public final class CommonsBukkitManifest extends HybridManifest { install(new LocalizationManifest()); install(new NavigatorManifest()); install(new RaindropManifest()); + install(new TokenManifest()); install(new PunishmentManifest()); // These are already bound as facets, so they only need to be exposed diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/event/InterfaceOpenEvent.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/event/InterfaceOpenEvent.java new file mode 100644 index 0000000..23b2d0d --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/event/InterfaceOpenEvent.java @@ -0,0 +1,36 @@ +package tc.oc.commons.bukkit.event; + + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import tc.oc.commons.bukkit.gui.Interface; + +public class InterfaceOpenEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + + private final Interface gui; + private final Player player; + + public InterfaceOpenEvent(Interface gui, Player player) { + this.gui = gui; + this.player = player; + } + + public Interface getInterface() { + return this.gui; + } + + public Player getPlayer() { + return this.player; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/Interface.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/Interface.java new file mode 100644 index 0000000..911b938 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/gui/Interface.java @@ -0,0 +1,75 @@ +package tc.oc.commons.bukkit.gui; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import tc.oc.commons.bukkit.gui.buttons.Button; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Interface { + + private Player player; + private List