diff options
Diffstat (limited to 'src/main/java/cat/freya/khs/game')
24 files changed, 2978 insertions, 0 deletions
diff --git a/src/main/java/cat/freya/khs/game/Board.java b/src/main/java/cat/freya/khs/game/Board.java new file mode 100644 index 0000000..e8e378d --- /dev/null +++ b/src/main/java/cat/freya/khs/game/Board.java @@ -0,0 +1,492 @@ +package cat.freya.khs.game; + +import cat.freya.khs.game.events.Border; +import cat.freya.khs.game.events.Glow; +import cat.freya.khs.game.events.Taunt; +import cat.freya.khs.game.util.Status; +import cat.freya.khs.Main; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Leaderboard.*; +import static cat.freya.khs.configuration.Localization.message; + +public class Board { + + private enum Type { + HIDER, + SEEKER, + SPECTATOR, + } + + private List<UUID> initialSeekers = null; + private final Map<UUID, Type> Players = new HashMap<>(); + private final Map<UUID, CustomBoard> customBoards = new HashMap<>(); + private final Map<UUID, Integer> hider_kills = new HashMap<>(), seeker_kills = new HashMap<>(), hider_deaths = new HashMap<>(), seeker_deaths = new HashMap<>(); + + public boolean contains(Player player) { + return Players.containsKey(player.getUniqueId()); + } + + public boolean containsUUID(UUID uuid) { + return Players.containsKey(uuid); + } + + public boolean isHider(Player player) { + return isHider(player.getUniqueId()); + } + + public boolean isHider(UUID uuid) { + if(!Players.containsKey(uuid)) return false; + return Players.get(uuid) == Type.HIDER; + } + + public boolean isSeeker(Player player) { + return isSeeker(player.getUniqueId()); + } + + public boolean isSeeker(UUID uuid) { + if(!Players.containsKey(uuid)) return false; + return Players.get(uuid) == Type.SEEKER; + } + + public boolean isSpectator(Player player) { + return isSpectator(player.getUniqueId()); + } + + public boolean isSpectator(UUID uuid) { + if(!Players.containsKey(uuid)) return false; + return Players.get(uuid) == Type.SPECTATOR; + } + + public int sizeHider() { + return getHiders().size(); + } + + public int sizeSeeker() { + return getSeekers().size(); + } + + public int size() { + return getPlayers().size(); + } + + public List<Player> getHiders() { + return Players.keySet().stream() + .filter(s -> Players.get(s) == Type.HIDER) + .map(Bukkit::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public List<Player> getSeekers() { + return Players.keySet().stream() + .filter(s -> Players.get(s) == Type.SEEKER) + .map(Bukkit::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public List<Player> getSpectators() { + return Players.keySet().stream() + .filter(s -> Players.get(s) == Type.SPECTATOR) + .map(Bukkit::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public List<Player> getPlayers() { + return Players.keySet().stream() + .map(Bukkit::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public void setInitialSeekers(List<UUID> seekers) { + initialSeekers = seekers; + } + + public List<Player> getInitialSeekers() { + if(initialSeekers == null) return null; + return initialSeekers.stream().map(u -> { + return Bukkit.getPlayer(u); + }).collect(Collectors.toList()); + } + + public Player getPlayer(UUID uuid) { + if(!Players.containsKey(uuid)) { + return null; + } + return Bukkit.getPlayer(uuid); + } + + public void addHider(Player player) { + Players.put(player.getUniqueId(), Type.HIDER); + } + + public void addSeeker(Player player) { + Players.put(player.getUniqueId(), Type.SEEKER); + } + + public void addSpectator(Player player) { + Players.put(player.getUniqueId(), Type.SPECTATOR); + } + + public void remove(Player player) { + Players.remove(player.getUniqueId()); + } + + public boolean onSameTeam(Player player1, Player player2) { + return Players.get(player1.getUniqueId()) == Players.get(player2.getUniqueId()); + } + + public void reload() { + Players.replaceAll((u, v) -> Type.HIDER); + hider_kills.clear(); + seeker_kills.clear(); + hider_deaths.clear(); + seeker_deaths.clear(); + } + + public void addKill(UUID uuid) { + if(Players.get(uuid) == Type.HIDER) { + int kills = hider_kills.getOrDefault(uuid, 0); + hider_kills.put(uuid, kills + 1); + } else if(Players.get(uuid) == Type.SEEKER) { + int kills = seeker_kills.getOrDefault(uuid, 0); + seeker_kills.put(uuid, kills + 1); + } + } + + public void addDeath(UUID uuid) { + if(Players.get(uuid) == Type.HIDER) { + int kills = hider_deaths.getOrDefault(uuid, 0); + hider_deaths.put(uuid, kills + 1); + } else if(Players.get(uuid) == Type.SEEKER) { + int kills = seeker_deaths.getOrDefault(uuid, 0); + seeker_deaths.put(uuid, kills + 1); + } + } + + public Map<UUID, Integer> getHiderKills() { + return new HashMap<>(hider_kills); + } + + public Map<UUID, Integer> getSeekerKills() { + return new HashMap<>(seeker_kills); + } + + public Map<UUID, Integer> getHiderDeaths() { + return new HashMap<>(hider_deaths); + } + + public Map<UUID, Integer> getSeekerDeaths() { + return new HashMap<>(seeker_deaths); + } + + public void createLobbyBoard(Player player) { + createLobbyBoard(player, true); + } + + private void createLobbyBoard(Player player, boolean recreate) { + CustomBoard board = customBoards.get(player.getUniqueId()); + if (recreate || board == null) { + board = new CustomBoard(player, LOBBY_TITLE); + board.updateTeams(); + } + int i=0; + for(String line : LOBBY_CONTENTS) { + if (line.equalsIgnoreCase("")) { + board.addBlank(); + } else if (line.contains("{COUNTDOWN}")) { + if (!lobbyCountdownEnabled) { + board.setLine(String.valueOf(i), line.replace("{COUNTDOWN}", COUNTDOWN_ADMINSTART)); + } else if (Main.getInstance().getGame().getLobbyTime() == -1) { + board.setLine(String.valueOf(i), line.replace("{COUNTDOWN}", COUNTDOWN_WAITING)); + } else { + board.setLine(String.valueOf(i), line.replace("{COUNTDOWN}", COUNTDOWN_COUNTING.replace("{AMOUNT}",Main.getInstance().getGame().getLobbyTime()+""))); + } + } else if (line.contains("{COUNT}")) { + board.setLine(String.valueOf(i), line.replace("{COUNT}", getPlayers().size()+"")); + } else if (line.contains("{SEEKER%}")) { + board.setLine(String.valueOf(i), line.replace("{SEEKER%}", getSeekerPercent()+"")); + } else if (line.contains("{HIDER%}")) { + board.setLine(String.valueOf(i), line.replace("{HIDER%}", getHiderPercent() + "")); + } else if (line.contains("{MAP}")) { + board.setLine(String.valueOf(i), line.replace("{MAP}", getMapName() + "")); + } else { + board.setLine(String.valueOf(i), line); + } + i++; + } + board.display(); + customBoards.put(player.getUniqueId(), board); + } + + public String getMapName() { + cat.freya.khs.configuration.Map map = Main.getInstance().getGame().getCurrentMap(); + if(map == null) return "Invalid"; + else return map.getName(); + } + + public void createGameBoard(Player player) { + createGameBoard(player, true); + } + + private void createGameBoard(Player player, boolean recreate) { + CustomBoard board = customBoards.get(player.getUniqueId()); + if (recreate || board == null) { + board = new CustomBoard(player, GAME_TITLE); + board.updateTeams(); + } + + int timeLeft = Main.getInstance().getGame().getTimeLeft(); + Status status = Main.getInstance().getGame().getStatus(); + + Taunt taunt = Main.getInstance().getGame().getTaunt(); + Border worldBorder = Main.getInstance().getGame().getCurrentMap().getWorldBorder(); + Glow glow = Main.getInstance().getGame().getGlow(); + + int i = 0; + for(String line : GAME_CONTENTS) { + if (line.equalsIgnoreCase("")) { + board.addBlank(); + } else { + if (line.contains("{TIME}")) { + String value = timeLeft/60 + "m" + timeLeft%60 + "s"; + board.setLine(String.valueOf(i), line.replace("{TIME}", value)); + } else if (line.contains("{TEAM}")) { + String value = getTeam(player); + board.setLine(String.valueOf(i), line.replace("{TEAM}", value)); + } else if (line.contains("{BORDER}")) { + if (!Main.getInstance().getGame().getCurrentMap().isWorldBorderEnabled()) continue; + if (status == Status.STARTING) { + board.setLine(String.valueOf(i), line.replace("{BORDER}", BORDER_COUNTING.replace("{AMOUNT}", "0"))); + } else if (!worldBorder.isRunning()) { + board.setLine(String.valueOf(i), line.replace("{BORDER}", BORDER_COUNTING.replaceFirst("\\{AMOUNT}", worldBorder.getDelay()/60+"").replaceFirst("\\{AMOUNT}", worldBorder.getDelay()%60+""))); + } else { + board.setLine(String.valueOf(i), line.replace("{BORDER}", BORDER_DECREASING)); + } + } else if (line.contains("{TAUNT}")) { + if (!tauntEnabled) continue; + if (taunt == null || status == Status.STARTING) { + board.setLine(String.valueOf(i), line.replace("{TAUNT}", TAUNT_COUNTING.replace("{AMOUNT}", "0"))); + } else if (!tauntLast && sizeHider() == 1) { + board.setLine(String.valueOf(i), line.replace("{TAUNT}", TAUNT_EXPIRED)); + } else if (!taunt.isRunning()) { + board.setLine(String.valueOf(i), line.replace("{TAUNT}", TAUNT_COUNTING.replaceFirst("\\{AMOUNT}", taunt.getDelay() / 60 + "").replaceFirst("\\{AMOUNT}", taunt.getDelay() % 60 + ""))); + } else { + board.setLine(String.valueOf(i), line.replace("{TAUNT}", TAUNT_ACTIVE)); + } + } else if (line.contains("{GLOW}")) { + if (!glowEnabled) continue; + if (glow == null || status == Status.STARTING || !glow.isRunning()) { + board.setLine(String.valueOf(i), line.replace("{GLOW}", GLOW_INACTIVE)); + } else { + board.setLine(String.valueOf(i), line.replace("{GLOW}", GLOW_ACTIVE)); + } + } else if (line.contains("{#SEEKER}")) { + board.setLine(String.valueOf(i), line.replace("{#SEEKER}", getSeekers().size()+"")); + } else if (line.contains("{#HIDER}")) { + board.setLine(String.valueOf(i), line.replace("{#HIDER}", getHiders().size()+"")); + } else if (line.contains("{MAP}")) { + board.setLine(String.valueOf(i), line.replace("{MAP}", getMapName() + "")); + } else { + board.setLine(String.valueOf(i), line); + } + } + i++; + } + board.display(); + customBoards.put(player.getUniqueId(), board); + } + + public void removeBoard(Player player) { + ScoreboardManager manager = Bukkit.getScoreboardManager(); + assert manager != null; + player.setScoreboard(manager.getMainScoreboard()); + customBoards.remove(player.getUniqueId()); + } + + public void reloadLobbyBoards() { + for(Player player : getPlayers()) + createLobbyBoard(player, false); + } + + public void reloadGameBoards() { + for(Player player : getPlayers()) + createGameBoard(player, false); + } + + public void reloadBoardTeams() { + for(CustomBoard board : customBoards.values()) + board.updateTeams(); + } + + private String getSeekerPercent() { + int size = size(); + if (size < 2) + return " --"; + else + return " "+(int)(100*(1.0/size)); + } + + private String getHiderPercent() { + int size = size(); + if (size < 2) + return " --"; + else + return " "+(int)(100-100*(1.0/size)); + } + + private String getTeam(Player player) { + if (isHider(player)) return message("HIDER_TEAM_NAME").toString(); + else if (isSeeker(player)) return message("SEEKER_TEAM_NAME").toString(); + else if (isSpectator(player)) return message("SPECTATOR_TEAM_NAME").toString(); + else return ChatColor.WHITE + "UNKNOWN"; + } + + public void cleanup() { + Players.clear();; + initialSeekers = null; + customBoards.clear(); + } + +} + +@SuppressWarnings("deprecation") +class CustomBoard { + + private final Scoreboard board; + private final Objective obj; + private final Player player; + private final Map<String,Line> LINES; + private int blanks; + private boolean displayed; + + public CustomBoard(Player player, String title) { + ScoreboardManager manager = Bukkit.getScoreboardManager(); + assert manager != null; + this.board = manager.getNewScoreboard(); + this.LINES = new HashMap<>(); + this.player = player; + if (Main.getInstance().supports(13)) { + this.obj = board.registerNewObjective( + "Scoreboard", "dummy", ChatColor.translateAlternateColorCodes('&', title)); + } else { + this.obj = board.registerNewObjective("Scoreboard", "dummy"); + this.obj.setDisplayName(ChatColor.translateAlternateColorCodes('&', title)); + } + this.blanks = 0; + this.displayed = false; + this.updateTeams(); + } + + public void updateTeams() { + try{ board.registerNewTeam("Hider"); } catch (Exception ignored) {} + try{ board.registerNewTeam("Seeker"); } catch (Exception ignored) {} + Team hiderTeam = board.getTeam("Hider"); + assert hiderTeam != null; + for(String entry : hiderTeam.getEntries()) + hiderTeam.removeEntry(entry); + for(Player player : Main.getInstance().getBoard().getHiders()) + hiderTeam.addEntry(player.getName()); + Team seekerTeam = board.getTeam("Seeker"); + assert seekerTeam != null; + for(String entry : seekerTeam.getEntries()) + seekerTeam.removeEntry(entry); + for(Player player : Main.getInstance().getBoard().getSeekers()) + seekerTeam.addEntry(player.getName()); + if (Main.getInstance().supports(9)) { + if (nameTagsVisible) { + hiderTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM); + seekerTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OTHER_TEAMS); + } else { + hiderTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER); + seekerTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER); + } + hiderTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); + seekerTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); + } else { + if (nameTagsVisible) { + hiderTeam.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OTHER_TEAMS); + seekerTeam.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OWN_TEAM); + } else { + hiderTeam.setNameTagVisibility(NameTagVisibility.NEVER); + seekerTeam.setNameTagVisibility(NameTagVisibility.NEVER); + } + } + hiderTeam.setPrefix(message("HIDER_TEAM_NAME").toString() + " " + ChatColor.RESET); + seekerTeam.setPrefix(message("SEEKER_TEAM_NAME").toString() + " " + ChatColor.RESET); + } + + public void setLine(String key, String message) { + Line line = LINES.get(key); + if (line == null) + addLine(key, ChatColor.translateAlternateColorCodes('&',message)); + else + updateLine(key, ChatColor.translateAlternateColorCodes('&',message)); + } + + private void addLine(String key, String message) { + Score score = obj.getScore(message); + score.setScore(LINES.values().size()+1); + Line line = new Line(LINES.values().size()+1, message); + LINES.put(key, line); + } + + public void addBlank() { + if (displayed) return; + StringBuilder temp = new StringBuilder(); + for(int i = 0; i <= blanks; i ++) + temp.append(ChatColor.RESET); + blanks++; + addLine("blank"+blanks, temp.toString()); + } + + private void updateLine(String key, String message) { + Line line = LINES.get(key); + board.resetScores(line.getMessage()); + line.setMessage(message); + Score newScore = obj.getScore(message); + + newScore.setScore(line.getScore()); + } + + public void display() { + displayed = true; + obj.setDisplaySlot(DisplaySlot.SIDEBAR); + player.setScoreboard(board); + } + +} + +class Line { + + private final int score; + private String message; + + public Line(int score, String message) { + this.score = score; + this.message = message; + } + + public int getScore() { + return score; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/main/java/cat/freya/khs/game/Disguiser.java b/src/main/java/cat/freya/khs/game/Disguiser.java new file mode 100644 index 0000000..4ec4e15 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/Disguiser.java @@ -0,0 +1,74 @@ +package cat.freya.khs.game; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Localization.message; + +import cat.freya.khs.game.util.Disguise; +import cat.freya.khs.configuration.Map; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; + +public class Disguiser { + + private final HashMap<Player, Disguise> disguises; + + public Disguiser(){ + this.disguises = new HashMap<>(); + + } + + public Disguise getDisguise(Player player){ + return disguises.get(player); + } + + public boolean disguised(Player player) { return disguises.containsKey(player); } + + @Nullable + public Disguise getByEntityID(int ID){ + return disguises.values().stream().filter(disguise -> disguise.getEntityID() == ID).findFirst().orElse(null); + } + + @Nullable + public Disguise getByHitBoxID(int ID){ + return disguises.values().stream().filter(disguise -> disguise.getHitBoxID() == ID).findFirst().orElse(null); + } + + public void check(){ + for(HashMap.Entry<Player, Disguise> set : disguises.entrySet()){ + Disguise disguise = set.getValue(); + Player player = set.getKey(); + if(!player.isOnline()) { + disguise.remove(); + disguises.remove(player); + } else { + disguise.update(); + } + } + } + + public void disguise(Player player, Material material, Map map){ + if(!map.isBlockHuntEnabled()){ + player.sendMessage(errorPrefix + message("BLOCKHUNT_DISABLED")); + return; + } + if(disguises.containsKey(player)){ + disguises.get(player).remove(); + } + Disguise disguise = new Disguise(player, material); + disguises.put(player, disguise); + } + + public void reveal(Player player){ + if(disguises.containsKey(player)) + disguises.get(player).remove(); + disguises.remove(player); + } + + public void cleanUp() { + disguises.values().forEach(Disguise::remove); + } + +} diff --git a/src/main/java/cat/freya/khs/game/EntityHider.java b/src/main/java/cat/freya/khs/game/EntityHider.java new file mode 100644 index 0000000..6224f17 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/EntityHider.java @@ -0,0 +1,326 @@ +package cat.freya.khs.game; + +import static com.comphenix.protocol.PacketType.Play.Server.*; + +import java.util.Collections; +import java.util.Map; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; + +public class EntityHider implements Listener { + protected Table<Integer, Integer, Boolean> observerEntityMap = HashBasedTable.create(); + + private static final PacketType[] ENTITY_PACKETS = { + ENTITY_EQUIPMENT, ANIMATION, NAMED_ENTITY_SPAWN, + COLLECT, SPAWN_ENTITY, SPAWN_ENTITY_EXPERIENCE_ORB, + ENTITY_VELOCITY, REL_ENTITY_MOVE, ENTITY_LOOK, + ENTITY_TELEPORT, ENTITY_HEAD_ROTATION, ENTITY_STATUS, ATTACH_ENTITY, ENTITY_METADATA, + ENTITY_EFFECT, REMOVE_ENTITY_EFFECT, BLOCK_BREAK_ANIMATION + }; + + public enum Policy { + WHITELIST, + BLACKLIST, + } + + private ProtocolManager manager; + + private final Listener bukkitListener; + private final PacketAdapter protocolListener; + + protected final Policy policy; + + public EntityHider(Plugin plugin, Policy policy) { + Preconditions.checkNotNull(plugin, "plugin cannot be NULL."); + + // Save policy + this.policy = policy; + this.manager = ProtocolLibrary.getProtocolManager(); + + // Register events and packet listener + plugin.getServer().getPluginManager().registerEvents( + bukkitListener = constructBukkit(), plugin); + manager.addPacketListener( + protocolListener = constructProtocol(plugin)); + } + + /** + * Set the visibility status of a given entity for a particular observer. + * @param observer - the observer player. + * @param entityID - ID of the entity that will be hidden or made visible. + * @param visible - TRUE if the entity should be made visible, FALSE if not. + * @return TRUE if the entity was visible before this method call, FALSE otherwise. + */ + protected boolean setVisibility(Player observer, int entityID, boolean visible) { + switch (policy) { + case BLACKLIST: + // Non-membership means they are visible + return !setMembership(observer, entityID, !visible); + case WHITELIST: + return setMembership(observer, entityID, visible); + default : + throw new IllegalArgumentException("Unknown policy: " + policy); + } + } + + /** + * Add or remove the given entity and observer entry from the table. + * @param observer - the player observer. + * @param newEntityId - ID of the entity. + * @param member - TRUE if they should be present in the table, FALSE otherwise. + * @return TRUE if they already were present, FALSE otherwise. + */ + protected boolean setMembership(Player observer, int newEntityId, boolean member) { + int entityID; + try { + entityID = observer.getEntityId(); + } catch (Exception e) { + return member; + } + if (member) { + return observerEntityMap.put(newEntityId, entityID, true) != null; + } else { + return observerEntityMap.remove(newEntityId, entityID) != null; + } + } + + /** + * Determine if the given entity and observer is present in the table. + * @param observer - the player observer. + * @param newEntityID - ID of the entity. + * @return TRUE if they are present, FALSE otherwise. + */ + protected boolean getMembership(Player observer, int newEntityID) { + int entityID; + try { + entityID = observer.getEntityId(); + } catch (Exception e) { + return false; + } + return observerEntityMap.contains(entityID, newEntityID); + } + + /** + * Determine if a given entity is visible for a particular observer. + * @param observer - the observer player. + * @param entityID - ID of the entity that we are testing for visibility. + * @return TRUE if the entity is visible, FALSE otherwise. + */ + protected boolean isVisible(Player observer, int entityID) { + // If we are using a whitelist, presence means visibility - if not, the opposite is the case + boolean presence = getMembership(observer, entityID); + + return (policy == Policy.WHITELIST) == presence; + } + + /** + * Remove the given entity from the underlying map. + * @param entity - the entity to remove. + */ + protected void removeEntity(Entity entity) { + int entityID; + try { + entityID = entity.getEntityId(); + } catch (Exception e) { + return; + } + + for (Map<Integer, Boolean> maps : observerEntityMap.rowMap().values()) { + maps.remove(entityID); + } + } + + /** + * Invoked when a player logs out. + * @param player - the player that jused logged out. + */ + protected void removePlayer(Player player) { + int entityID; + try { + entityID = player.getEntityId(); + } catch (Exception e) { + return; + } + observerEntityMap.rowMap().remove(entityID); + } + + /** + * Construct the Bukkit event listener. + * @return Our listener. + */ + private Listener constructBukkit() { + return new Listener() { + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + removeEntity(e.getEntity()); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent e) { + for (Entity entity : e.getChunk().getEntities()) { + removeEntity(entity); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + removePlayer(e.getPlayer()); + } + }; + } + + /** + * Construct the packet listener that will be used to intercept every entity-related packet. + * @param plugin - the parent plugin. + * @return The packet listener. + */ + private PacketAdapter constructProtocol(Plugin plugin) { + return new PacketAdapter(plugin, ENTITY_PACKETS) { + @Override + public void onPacketSending(PacketEvent event) { + int entityID = event.getPacket().getIntegers().read(0); + + // See if this packet should be cancelled + if (!isVisible(event.getPlayer(), entityID)) { + event.setCancelled(true); + } + } + }; + } + + /** + * Toggle the visibility status of an entity for a player. + * <p> + * If the entity is visible, it will be hidden. If it is hidden, it will become visible. + * @param observer - the player observer. + * @param entity - the entity to toggle. + * @return TRUE if the entity was visible before, FALSE otherwise. + */ + @SuppressWarnings("unused") + public final boolean toggleEntity(Player observer, Entity entity) { + int entityID; + try { + entityID = observer.getEntityId(); + } catch (Exception e) { + return true; + } + if (isVisible(observer, entityID)) { + return hideEntity(observer, entity); + } else { + return !showEntity(observer, entity); + } + } + + /** + * Allow the observer to see an entity that was previously hidden. + * @param observer - the observer. + * @param entity - the entity to show. + * @return TRUE if the entity was hidden before, FALSE otherwise. + */ + public final boolean showEntity(Player observer, Entity entity) { + validate(observer, entity); + int entityID; + try { + entityID = entity.getEntityId(); + } catch (Exception e) { + return false; + } + boolean hiddenBefore = !setVisibility(observer, entityID, true); + + // Resend packets + if (manager != null && hiddenBefore) { + manager.updateEntity(entity, Collections.singletonList(observer)); + } + return hiddenBefore; + } + + /** + * Prevent the observer from seeing a given entity. + * @param observer - the player observer. + * @param entity - the entity to hide. + * @return TRUE if the entity was previously visible, FALSE otherwise. + */ + public final boolean hideEntity(Player observer, Entity entity) { + validate(observer, entity); + int entityID; + try { + entityID = entity.getEntityId(); + } catch (Exception e) { + return true; + } + boolean visibleBefore = setVisibility(observer, entityID, false); + + if (visibleBefore) { + PacketContainer destroyEntity = new PacketContainer(ENTITY_DESTROY); + try { + destroyEntity.getIntegerArrays().write(0, new int[]{entityID}); + } catch (Exception e){ return false; } + // Make the entity disappear + manager.sendServerPacket(observer, destroyEntity); + } + return visibleBefore; + } + + /** + * Determine if the given entity has been hidden from an observer. + * <p> + * Note that the entity may very well be occluded or out of range from the perspective + * of the observer. This method simply checks if an entity has been completely hidden + * for that observer. + * @param observer - the observer. + * @param entity - the entity that may be hidden. + * @return TRUE if the player may see the entity, FALSE if the entity has been hidden. + */ + @SuppressWarnings("unused") + public final boolean canSee(Player observer, Entity entity) { + validate(observer, entity); + int entityID; + try { + entityID = entity.getEntityId(); + } catch (Exception e) { + return true; + } + return isVisible(observer, entityID); + } + + private void validate(Player observer, Entity entity) { + Preconditions.checkNotNull(observer, "observer cannot be NULL."); + Preconditions.checkNotNull(entity, "entity cannot be NULL."); + } + + /** + * Retrieve the current visibility policy. + * @return The current visibility policy. + */ + @SuppressWarnings("unused") + public Policy getPolicy() { + return policy; + } + + @SuppressWarnings("unused") + public void close() { + if (manager != null) { + HandlerList.unregisterAll(bukkitListener); + manager.removePacketListener(protocolListener); + manager = null; + } + } +} diff --git a/src/main/java/cat/freya/khs/game/Game.java b/src/main/java/cat/freya/khs/game/Game.java new file mode 100644 index 0000000..69e9b82 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/Game.java @@ -0,0 +1,407 @@ +/* + * This file is part of Kenshins Hide and Seek + * + * Copyright (c) 2020-2021. Tyler Murphy + * + * Kenshins Hide and Seek free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * he Free Software Foundation version 3. + * + * Kenshins Hide and Seek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package cat.freya.khs.game; + +import com.cryptomorin.xseries.messages.ActionBar; +import com.cryptomorin.xseries.messages.Titles; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import cat.freya.khs.game.events.Glow; +import cat.freya.khs.game.events.Taunt; +import cat.freya.khs.game.listener.RespawnHandler; +import cat.freya.khs.game.util.CountdownDisplay; +import cat.freya.khs.game.util.Status; +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Map; +import cat.freya.khs.configuration.Maps; +import cat.freya.khs.game.util.WinType; +import org.bukkit.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.stream.Collectors; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Localization.message; + +public class Game { + + private final Taunt taunt; + private final Glow glow; + + private final Board board; + + private Status status; + + private Map currentMap; + + private int gameTick; + private int lobbyTimer; + private int startingTimer; + private int gameTimer; + private boolean hiderLeft; + + public Game(Map map, Board board) { + + this.currentMap = map; + + this.taunt = new Taunt(); + this.glow = new Glow(); + + this.status = Status.STANDBY; + + this.board = board; + + this.gameTick = 0; + this.lobbyTimer = -1; + this.startingTimer = -1; + this.gameTimer = 0; + this.hiderLeft = false; + } + + public Status getStatus(){ + return status; + } + + public int getTimeLeft(){ + return gameTimer; + } + + public int getLobbyTime(){ + return lobbyTimer; + } + + public Glow getGlow(){ + return glow; + } + + public Taunt getTaunt(){ + return taunt; + } + + public void start() { + List<Player> seekers = new ArrayList<>(startingSeekerCount); + List<Player> pool = board.getPlayers(); + for (int i = 0; i < startingSeekerCount; i++) { + try { + int rand = (int)(Math.random() * pool.size()); + seekers.add(pool.remove(rand)); + } catch (Exception e){ + Main.getInstance().getLogger().warning("Failed to select random seeker."); + return; + } + } + start(seekers); + } + + public void start(List<Player> seekers) { + if (mapSaveEnabled) currentMap.getWorldLoader().rollback(); + board.reload(); + board.setInitialSeekers(seekers.stream().map(Player::getUniqueId).collect(Collectors.toList())); + seekers.forEach(seeker -> { + board.addSeeker(seeker); + PlayerLoader.loadSeeker(seeker, currentMap); + }); + board.getPlayers().forEach(player -> { + if(board.isSeeker(player)) return; + board.addHider(player); + PlayerLoader.loadHider(player, currentMap); + }); + board.getPlayers().forEach(board::createGameBoard); + currentMap.getWorldBorder().resetWorldBorder(); + if (gameLength > 0) gameTimer = gameLength; + status = Status.STARTING; + startingTimer = hidingTimer; + } + + private void stop(WinType type) { + status = Status.ENDING; + List<UUID> players = board.getPlayers().stream().map(Entity::getUniqueId).collect(Collectors.toList()); + if (type == WinType.HIDER_WIN) { + List<UUID> winners = board.getHiders().stream().map(Entity::getUniqueId).collect(Collectors.toList()); + Main.getInstance().getDatabase().getGameData().addWins(board, players, winners, board.getHiderKills(), board.getHiderDeaths(), board.getSeekerKills(), board.getSeekerDeaths(), type); + } else if (type == WinType.SEEKER_WIN) { + List<UUID> winners = new ArrayList<>(); + board.getInitialSeekers().forEach(p -> { + winners.add(p.getUniqueId()); + }); + if (!waitTillNoneLeft && board.getHiders().size() == 1) { + winners.add(board.getHiders().get(0).getUniqueId()); + } + Main.getInstance().getDatabase().getGameData().addWins(board, players, winners, board.getHiderKills(), board.getHiderDeaths(), board.getSeekerKills(), board.getSeekerDeaths(), type); + } + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), this::end, endGameDelay*20); + } + + public void end() { + board.getPlayers().forEach(PlayerLoader::unloadPlayer); + currentMap.getWorldBorder().resetWorldBorder(); + Map nextMap = Maps.getRandomMap(); + if(nextMap != null) this.currentMap = nextMap; + board.getPlayers().forEach(player -> { + if (leaveOnEnd) { + board.removeBoard(player); + board.remove(player); + handleBungeeLeave(player); + } else { + currentMap.getLobby().teleport(player); + board.createLobbyBoard(player); + board.addHider(player); + PlayerLoader.joinPlayer(player, currentMap); + } + }); + RespawnHandler.temp_loc.clear(); + if (mapSaveEnabled) currentMap.getWorldLoader().unloadMap(); + board.reloadLobbyBoards(); + status = Status.ENDED; + } + + public void join(Player player) { + if (status != Status.STARTING && status != Status.PLAYING) { + if(saveInventory) { + ItemStack[] data = player.getInventory().getContents(); + Main.getInstance().getDatabase().getInventoryData().saveInventory(player.getUniqueId(), data); + } + PlayerLoader.joinPlayer(player, currentMap); + board.addHider(player); + board.createLobbyBoard(player); + board.reloadLobbyBoards(); + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(messagePrefix + message("GAME_JOIN").addPlayer(player)); + else broadcastMessage(messagePrefix + message("GAME_JOIN").addPlayer(player)); + } else { + PlayerLoader.loadSpectator(player, currentMap); + board.addSpectator(player); + board.createGameBoard(player); + player.sendMessage(messagePrefix + message("GAME_JOIN_SPECTATOR")); + } + } + + public void leave(Player player) { + PlayerLoader.unloadPlayer(player); + if(saveInventory) { + ItemStack[] data = Main.getInstance().getDatabase().getInventoryData().getInventory(player.getUniqueId()); + try { + player.getInventory().setContents(data); + } catch (NullPointerException ignored){} + } + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(messagePrefix + message("GAME_LEAVE").addPlayer(player)); + else broadcastMessage(messagePrefix + message("GAME_LEAVE").addPlayer(player)); + if (board.isHider(player) && status != Status.ENDING && status != Status.STANDBY) { + hiderLeft = true; + } + board.removeBoard(player); + board.remove(player); + if (status == Status.STANDBY) { + board.reloadLobbyBoards(); + } else { + board.reloadGameBoards(); + board.reloadBoardTeams(); + } + handleBungeeLeave(player); + } + + @SuppressWarnings("UnstableApiUsage") + private void handleBungeeLeave(Player player) { + if (bungeeLeave) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(leaveServer); + player.sendPluginMessage(Main.getInstance(), "BungeeCord", out.toByteArray()); + } else { + exitPosition.teleport(player); + } + } + + public void onTick() { + if (currentMap == null || currentMap.isNotSetup()) return; + if (status == Status.STANDBY) whileWaiting(); + else if (status == Status.STARTING) whileStarting(); + else if (status == Status.PLAYING) whilePlaying(); + gameTick++; + } + + private void whileWaiting() { + if (!lobbyCountdownEnabled) return; + if (lobbyMin <= board.size()) { + if (lobbyTimer < 0) + lobbyTimer = countdown; + if (board.size() >= changeCountdown) + lobbyTimer = Math.min(lobbyTimer, 10); + if (gameTick % 20 == 0) { + lobbyTimer--; + board.reloadLobbyBoards(); + } + if (lobbyTimer == 0) { + start(); + } + } else { + lobbyTimer = -1; + if (gameTick % 20 == 0) { + board.reloadLobbyBoards(); + } + } + } + + private void whileStarting() { + if(gameTick % 20 == 0) { + if (startingTimer % 5 == 0 || startingTimer < 5) { + String message; + if (startingTimer == 0) { + message = message("START").toString(); + status = Status.PLAYING; + board.getPlayers().forEach(player -> { + PlayerLoader.resetPlayer(player, board); + if(board.isSeeker(player)){ + currentMap.getGameSpawn().teleport(player); + } + }); + } else if (startingTimer == 1){ + message = message("START_COUNTDOWN_LAST").addAmount(startingTimer).toString(); + } else { + message = message("START_COUNTDOWN").addAmount(startingTimer).toString(); + } + board.getPlayers().forEach(player -> { + if (countdownDisplay == CountdownDisplay.CHAT) { + player.sendMessage(messagePrefix + message); + } else if (countdownDisplay == CountdownDisplay.ACTIONBAR) { + ActionBar.clearActionBar(player); + ActionBar.sendActionBar(player, messagePrefix + message); + } else if (countdownDisplay == CountdownDisplay.TITLE && startingTimer != 30) { + Titles.clearTitle(player); + Titles.sendTitle(player, 10, 40, 10, " ", message); + } + }); + } + startingTimer--; + } + checkWinConditions(); + } + + private void whilePlaying() { + for(Player hider : board.getHiders()) { + int distance = 100, temp = 100; + for(Player seeker : board.getSeekers()) { + try { + temp = (int) hider.getLocation().distance(seeker.getLocation()); + } catch (Exception e) { + //Players in different worlds, NOT OK!!! + } + if (distance > temp) { + distance = temp; + } + } + if (seekerPing) switch(gameTick %10) { + case 0: + if (distance < seekerPingLevel1) heartbeatSound.play(hider, seekerPingLeadingVolume, seekerPingPitch); + if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch); + break; + case 3: + if (distance < seekerPingLevel1) heartbeatSound.play(hider, seekerPingVolume, seekerPingPitch); + if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch); + break; + case 6: + if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch); + break; + case 9: + if (distance < seekerPingLevel2) ringingSound.play(hider, seekerPingVolume, seekerPingPitch); + break; + } + } + if (gameTick %20 == 0) { + if (gameLength > 0) { + board.reloadGameBoards(); + gameTimer--; + } + if (currentMap.isWorldBorderEnabled()) currentMap.getWorldBorder().update(); + if (tauntEnabled) taunt.update(); + if (glowEnabled || alwaysGlow) glow.update(); + } + board.getSpectators().forEach(spectator -> spectator.setFlying(spectator.getAllowFlight())); + checkWinConditions(); + } + + public void broadcastMessage(String message) { + for(Player player : board.getPlayers()) { + player.sendMessage(message); + } + } + + public void broadcastTitle(String title, String subtitle) { + for (Player player : board.getPlayers()) { + Titles.sendTitle(player, 10, 70, 20, title, subtitle); + } + } + + public boolean isCurrentMapValid() { + return currentMap != null && !currentMap.isNotSetup(); + } + + public boolean checkCurrentMap() { + if(currentMap != null && !currentMap.isNotSetup()) return false; + this.currentMap = Maps.getRandomMap(); + return this.currentMap == null; + } + + public void setCurrentMap(Map map) { + this.currentMap = map; + } + + public Map getCurrentMap() { + return currentMap; + } + + private void checkWinConditions() { + int hiderCount = board.sizeHider(); + if (hiderCount < 1 || (!waitTillNoneLeft && hiderCount < 2)) { + if (hiderLeft && dontRewardQuit) { + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT")); + else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT")); + if (gameOverTitle) broadcastTitle(message("GAME_TITLE_NO_WIN").toString(), message("GAME_GAMEOVER_HIDERS_QUIT").toString()); + stop(WinType.NONE); + } else { + if (hiderCount < 1 || waitTillNoneLeft) { + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND")); + else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND")); + if (gameOverTitle) broadcastTitle(message("GAME_TITLE_SEEKERS_WIN").toString(), message("GAME_GAMEOVER_HIDERS_FOUND").toString()); + } else { + Player hider = board.getHiders().get(0); + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_LAST_HIDER").addPlayer(hider)); + else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_LAST_HIDER").addPlayer(hider)); + if (gameOverTitle) broadcastTitle(message("GAME_TITLE_SINGLE_HIDER_WIN").addPlayer(hider).toString(), message("GAME_SUBTITLE_SINGLE_HIDER_WIN").addPlayer(hider).toString()); + } + stop(WinType.SEEKER_WIN); + } + } else if (board.sizeSeeker() < 1) { + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(abortPrefix + message("GAME_GAMEOVER_SEEKERS_QUIT")); + else broadcastMessage(abortPrefix + message("GAME_GAMEOVER_SEEKERS_QUIT")); + if (gameOverTitle) broadcastTitle(message("GAME_TITLE_NO_WIN").toString(), message("GAME_GAMEOVER_SEEKERS_QUIT").toString()); + stop(dontRewardQuit ? WinType.NONE : WinType.HIDER_WIN); + } else if (gameTimer < 1) { + if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME")); + else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME")); + if (gameOverTitle) broadcastTitle(message("GAME_TITLE_HIDERS_WIN").toString(), message("GAME_GAMEOVER_TIME").toString()); + stop(WinType.HIDER_WIN); + } + hiderLeft = false; + } + +} diff --git a/src/main/java/cat/freya/khs/game/PlayerLoader.java b/src/main/java/cat/freya/khs/game/PlayerLoader.java new file mode 100644 index 0000000..cb606a1 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/PlayerLoader.java @@ -0,0 +1,187 @@ +/* + * This file is part of Kenshins Hide and Seek + * + * Copyright (c) 2022 Tyler Murphy. + * + * Kenshins Hide and Seek free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * he Free Software Foundation version 3. + * + * Kenshins Hide and Seek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package cat.freya.khs.game; + +import com.cryptomorin.xseries.messages.Titles; +import net.md_5.bungee.api.ChatColor; +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Items; +import cat.freya.khs.configuration.Map; +import org.bukkit.GameMode; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Items.HIDER_ITEMS; +import static cat.freya.khs.configuration.Items.SEEKER_ITEMS; +import static cat.freya.khs.configuration.Localization.message; + +@SuppressWarnings("deprecation") +public class PlayerLoader { + + public static void loadHider(Player player, Map map){ + map.getGameSpawn().teleport(player); + loadPlayer(player); + player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,1000000,5,false,false)); + Titles.sendTitle(player, 10, 70, 20, ChatColor.WHITE + "" + message("HIDER_TEAM_NAME"), ChatColor.WHITE + message("HIDERS_SUBTITLE").toString()); + if(map.isBlockHuntEnabled()){ + openBlockHuntPicker(player, map); + } + } + + public static void loadSeeker(Player player, Map map){ + map.getGameSeekerLobby().teleport(player); + loadPlayer(player); + Titles.sendTitle(player, 10, 70, 20, ChatColor.WHITE + "" + message("SEEKER_TEAM_NAME"), ChatColor.WHITE + message("SEEKERS_SUBTITLE").toString()); + } + + public static void loadSpectator(Player player, Map map){ + map.getGameSpawn().teleport(player); + loadPlayer(player); + player.setAllowFlight(true); + player.setFlying(true); + player.setFallDistance(0.0F); + player.getInventory().setItem(flightToggleItemPosition, flightToggleItem); + player.getInventory().setItem(teleportItemPosition, teleportItem); + Main.getInstance().getBoard().getPlayers().forEach(otherPlayer -> otherPlayer.hidePlayer(player)); + Titles.sendTitle(player, 10, 70, 20, ChatColor.GRAY + "" + ChatColor.BOLD + "SPECTATING", ChatColor.WHITE + message("SPECTATOR_SUBTITLE").toString()); + } + + public static void loadDeadHiderSpectator(Player player, Map map) { + map.getGameSpawn().teleport(player); + loadPlayer(player); + player.setAllowFlight(true); + player.setFlying(true); + player.setFallDistance(0.0F); + player.getInventory().setItem(flightToggleItemPosition, flightToggleItem); + player.getInventory().setItem(teleportItemPosition, teleportItem); + Main.getInstance().getBoard().getPlayers().forEach(otherPlayer -> otherPlayer.hidePlayer(player)); + } + + public static void resetPlayer(Player player, Board board){ + if(board.isSpectator(player)) return; + loadPlayer(player); + if (board.isSeeker(player)) { + if (pvpEnabled) { + for(int i = 0; i < 9; i++) { + if (SEEKER_ITEMS.get(i) == null) continue; + player.getInventory().setItem(i, SEEKER_ITEMS.get(i)); + } + if (Items.SEEKER_HELM != null) + player.getInventory().setHelmet(Items.SEEKER_HELM); + if (Items.SEEKER_CHEST != null) + player.getInventory().setChestplate(Items.SEEKER_CHEST); + if (Items.SEEKER_LEGS != null) + player.getInventory().setLeggings(Items.SEEKER_LEGS); + if (Items.SEEKER_BOOTS != null) + player.getInventory().setBoots(Items.SEEKER_BOOTS); + } + for(PotionEffect effect : Items.SEEKER_EFFECTS) + player.addPotionEffect(effect); + } else if (board.isHider(player)) { + if (pvpEnabled) { + for(int i = 0; i < 9; i++) { + if (HIDER_ITEMS.get(i) == null) continue; + player.getInventory().setItem(i, HIDER_ITEMS.get(i)); + } + if (Items.HIDER_HELM != null) + player.getInventory().setHelmet(Items.HIDER_HELM); + if (Items.HIDER_CHEST != null) + player.getInventory().setChestplate(Items.HIDER_CHEST); + if (Items.HIDER_LEGS != null) + player.getInventory().setLeggings(Items.HIDER_LEGS); + if (Items.HIDER_BOOTS != null) + player.getInventory().setBoots(Items.HIDER_BOOTS); + } + for(PotionEffect effect : Items.HIDER_EFFECTS) + player.addPotionEffect(effect); + if (glowEnabled) { + player.getInventory().addItem(glowPowerupItem); + } + } + } + + public static void unloadPlayer(Player player){ + player.setGameMode(GameMode.ADVENTURE); + player.getInventory().clear(); + Main.getInstance().getDisguiser().reveal(player); + for(PotionEffect effect : player.getActivePotionEffects()) { + player.removePotionEffect(effect.getType()); + } + if (Main.getInstance().supports(9)) { + AttributeInstance attribute = player.getAttribute(Attribute.GENERIC_MAX_HEALTH); + if (attribute != null) player.setHealth(attribute.getValue()); + for(Player temp : Main.getInstance().getBoard().getPlayers()) { + Main.getInstance().getGame().getGlow().setGlow(player, temp, false); + } + } else { + player.setHealth(player.getMaxHealth()); + } + Main.getInstance().getBoard().getPlayers().forEach(temp -> { + player.showPlayer(temp); + temp.showPlayer(player); + }); + player.setAllowFlight(false); + player.setFlying(false); + player.setFallDistance(0.0F); + } + + public static void joinPlayer(Player player, Map map){ + map.getLobby().teleport(player); + loadPlayer(player); + if (lobbyStartItem != null && (!lobbyItemStartAdmin || player.hasPermission("hideandseek.start"))) + player.getInventory().setItem(lobbyItemStartPosition, lobbyStartItem); + if (lobbyLeaveItem != null) + player.getInventory().setItem(lobbyItemLeavePosition, lobbyLeaveItem); + } + + private static void loadPlayer(Player player){ + player.setFlying(false); + player.setAllowFlight(false); + player.setGameMode(GameMode.ADVENTURE); + player.getInventory().clear(); + for(PotionEffect effect : player.getActivePotionEffects()) { + if(effect.getType().getName().equals("INVISIBILITY") && Main.getInstance().getDisguiser().disguised(player)) continue; + player.removePotionEffect(effect.getType()); + } + player.setFoodLevel(20); + if (Main.getInstance().supports(9)) { + AttributeInstance attribute = player.getAttribute(Attribute.GENERIC_MAX_HEALTH); + if (attribute != null) player.setHealth(attribute.getValue()); + } else { + player.setHealth(player.getMaxHealth()); + } + } + + public static void openBlockHuntPicker(Player player, Map map){ + int slots = ((map.getBlockHunt().size()-1)/9)*9+9; + Inventory inventory = Main.getInstance().getServer().createInventory(null, slots, "Select a Block: " + map.getName()); + for(int i=0;i<map.getBlockHunt().size();i++){ + inventory.setItem(i, new ItemStack(map.getBlockHunt().get(i))); + } + player.openInventory(inventory); + } + +} diff --git a/src/main/java/cat/freya/khs/game/events/Border.java b/src/main/java/cat/freya/khs/game/events/Border.java new file mode 100644 index 0000000..87f9845 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/events/Border.java @@ -0,0 +1,72 @@ +package cat.freya.khs.game.events; + +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Map; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Localization.message; + +public class Border { + + private int delay; + private boolean running; + private final Map map; + private int currentSize; + + public Border(Map map) { + this.map = map; + this.delay = (int) (60 * map.getWorldBorderData().getY()); + this.currentSize = (int) map.getWorldBorderData().getX(); + } + + public void update() { + if (delay == 30 && !running) { + Main.getInstance().getGame().broadcastMessage(worldBorderPrefix + message("WORLDBORDER_WARN")); + } else if (delay == 0) { + if (running) { + delay = (int) (60 * map.getWorldBorderData().getY()); + running = false; + } + else decreaseWorldBorder(); + } + delay--; + } + + private void decreaseWorldBorder() { + if (currentSize == 100) return; + if(map.getGameSpawn().load() == null) return; + int change = (int) map.getWorldBorderData().getZ(); + if (currentSize-change < 100) { + change = currentSize-100; + } + running = true; + Main.getInstance().getGame().broadcastMessage(worldBorderPrefix + message("WORLDBORDER_DECREASING").addAmount(change)); + currentSize -= map.getWorldBorderData().getZ(); + org.bukkit.WorldBorder border = map.getGameSpawn().load().getWorldBorder(); + border.setSize(border.getSize()-change,30); + delay = 30; + } + + public void resetWorldBorder() { + if(map.getGameSpawn().load() == null) return; + org.bukkit.WorldBorder border = map.getGameSpawn().load().getWorldBorder(); + if (map.isWorldBorderEnabled()) { + border.setSize(map.getWorldBorderData().getX()); + border.setCenter(map.getWorldBorderPos().getX(), map.getWorldBorderPos().getY()); + currentSize = (int) map.getWorldBorderData().getX(); + } else { + border.setSize(30000000); + border.setCenter(0, 0); + } + delay = (int) (60 * map.getWorldBorderData().getY()); + } + + public int getDelay() { + return delay; + } + + public boolean isRunning() { + return running; + } + +}
\ No newline at end of file diff --git a/src/main/java/cat/freya/khs/game/events/Glow.java b/src/main/java/cat/freya/khs/game/events/Glow.java new file mode 100644 index 0000000..8680b61 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/events/Glow.java @@ -0,0 +1,72 @@ +package cat.freya.khs.game.events; + +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import cat.freya.khs.util.packet.EntityMetadataPacket; +import cat.freya.khs.Main; +import org.bukkit.entity.Player; + +import static cat.freya.khs.configuration.Config.*; + +public class Glow { + + private static final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + private int glowTime; + private boolean running; + + public Glow() { + this.glowTime = 0; + } + + public void onProjectile() { + if (glowStackable) glowTime += glowLength; + else glowTime = glowLength; + running = true; + } + + private void sendPackets() { + for (Player hider : Main.getInstance().getBoard().getHiders()) + for (Player seeker : Main.getInstance().getBoard().getSeekers()) + setGlow(hider, seeker, true); + } + + public void update() { + if(alwaysGlow){ + sendPackets(); + return; + } + if (running) { + sendPackets(); + glowTime--; + glowTime = Math.max(glowTime, 0); + if (glowTime == 0) { + stopGlow(); + } + } + } + + private void stopGlow() { + running = false; + for (Player hider : Main.getInstance().getBoard().getHiders()) { + for (Player seeker : Main.getInstance().getBoard().getSeekers()) { + setGlow(hider, seeker, false); + } + } + } + + public boolean isRunning() { + return running; + } + + public void setGlow(Player player, Player target, boolean glowing) { + + EntityMetadataPacket packet = new EntityMetadataPacket(); + packet.setEntity(target); + packet.setGlow(glowing); + packet.writeMetadata(); + packet.send(player); + + } + +} diff --git a/src/main/java/cat/freya/khs/game/events/Taunt.java b/src/main/java/cat/freya/khs/game/events/Taunt.java new file mode 100644 index 0000000..931c802 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/events/Taunt.java @@ -0,0 +1,101 @@ +package cat.freya.khs.game.events; + +import cat.freya.khs.Main; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; +import org.bukkit.entity.Player; +import org.bukkit.inventory.meta.FireworkMeta; + +import java.util.Optional; +import java.util.Random; +import java.util.UUID; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Config.tauntDelay; +import static cat.freya.khs.configuration.Localization.message; + +public class Taunt { + + private UUID tauntPlayer; + private int delay; + private boolean running; + + public Taunt() { + this.delay = tauntDelay; + } + + public void update() { + if (delay == 0) { + if (running) launchTaunt(); + else if (tauntLast || Main.getInstance().getBoard().sizeHider() > 1) executeTaunt(); + } else { + delay--; + delay = Math.max(delay, 0); + } + } + + private void executeTaunt() { + Optional<Player> rand = Main.getInstance().getBoard().getHiders().stream().skip(new Random().nextInt(Main.getInstance().getBoard().size())).findFirst(); + if (!rand.isPresent()) { + Main.getInstance().getLogger().warning("Failed to select random seeker."); + return; + } + Player taunted = rand.get(); + taunted.sendMessage(message("TAUNTED").toString()); + Main.getInstance().getGame().broadcastMessage(tauntPrefix + message("TAUNT")); + tauntPlayer = taunted.getUniqueId(); + running = true; + delay = 30; + } + + private void launchTaunt() { + Player taunted = Main.getInstance().getBoard().getPlayer(tauntPlayer); + if (taunted != null) { + if (!Main.getInstance().getBoard().isHider(taunted)) { + Main.getInstance().getLogger().info("Taunted played died and is now seeker. Skipping taunt."); + tauntPlayer = null; + running = false; + delay = tauntDelay; + return; + } + World world = taunted.getLocation().getWorld(); + if (world == null) { + Main.getInstance().getLogger().severe("Game world is null while trying to launch taunt."); + tauntPlayer = null; + running = false; + delay = tauntDelay; + return; + } + Firework fw = (Firework) world.spawnEntity(taunted.getLocation(), EntityType.FIREWORK); + FireworkMeta fwm = fw.getFireworkMeta(); + fwm.setPower(4); + fwm.addEffect(FireworkEffect.builder() + .withColor(Color.BLUE) + .withColor(Color.RED) + .withColor(Color.YELLOW) + .with(FireworkEffect.Type.STAR) + .with(FireworkEffect.Type.BALL) + .with(FireworkEffect.Type.BALL_LARGE) + .flicker(true) + .withTrail() + .build()); + fw.setFireworkMeta(fwm); + Main.getInstance().getGame().broadcastMessage(tauntPrefix + message("TAUNT_ACTIVATE")); + } + tauntPlayer = null; + running = false; + delay = tauntDelay; + } + + public int getDelay() { + return delay; + } + + public boolean isRunning() { + return running; + } + +}
\ No newline at end of file diff --git a/src/main/java/cat/freya/khs/game/listener/BlockedCommandHandler.java b/src/main/java/cat/freya/khs/game/listener/BlockedCommandHandler.java new file mode 100644 index 0000000..dac75b0 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/BlockedCommandHandler.java @@ -0,0 +1,36 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import cat.freya.khs.game.util.Status; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +import static cat.freya.khs.configuration.Config.blockedCommands; +import static cat.freya.khs.configuration.Config.errorPrefix; +import static cat.freya.khs.configuration.Localization.message; + +public class BlockedCommandHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerCommand(PlayerCommandPreprocessEvent event) { + Player player = event.getPlayer(); + String message = event.getMessage(); + String[] array = message.split(" "); + String[] temp = array[0].split(":"); + for(String handle : blockedCommands) { + if ( + array[0].substring(1).equalsIgnoreCase(handle) && Main.getInstance().getBoard().contains(player) || + temp[temp.length-1].equalsIgnoreCase(handle) && Main.getInstance().getBoard().contains(player) + ) { + if (Main.getInstance().getGame().getStatus() == Status.STANDBY) return; + player.sendMessage(errorPrefix + message("BLOCKED_COMMAND")); + event.setCancelled(true); + break; + } + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/ChatHandler.java b/src/main/java/cat/freya/khs/game/listener/ChatHandler.java new file mode 100644 index 0000000..3ff8539 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/ChatHandler.java @@ -0,0 +1,20 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +public class ChatHandler implements Listener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onChat(AsyncPlayerChatEvent event) { + if (Main.getInstance().getBoard().isSpectator(event.getPlayer())) { + event.setCancelled(true); + Main.getInstance().getBoard().getSpectators().forEach(spectator -> spectator.sendMessage(ChatColor.GRAY + "[SPECTATOR] " + event.getPlayer().getName() + ": " + event.getMessage())); + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/DamageHandler.java b/src/main/java/cat/freya/khs/game/listener/DamageHandler.java new file mode 100644 index 0000000..898f021 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/DamageHandler.java @@ -0,0 +1,136 @@ +package cat.freya.khs.game.listener; + +import com.cryptomorin.xseries.XSound; +import cat.freya.khs.Main; +import cat.freya.khs.game.Board; +import cat.freya.khs.game.Game; +import cat.freya.khs.game.PlayerLoader; +import cat.freya.khs.game.util.Status; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PlayerDeathEvent; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Localization.message; + +public class DamageHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onEntityDamage(EntityDamageEvent event) { + Board board = Main.getInstance().getBoard(); + Game game = Main.getInstance().getGame(); + // If you are not a player, get out of here + if (!(event.getEntity() instanceof Player)) return; + // Define variables + Player player = (Player) event.getEntity(); + Player attacker = null; + // If map is not setup we won't be able to process on it :o + if (!game.isCurrentMapValid()) { return; } + // If there is an attacker, find them + if (event instanceof EntityDamageByEntityEvent) { + if (((EntityDamageByEntityEvent) event).getDamager() instanceof Player) + attacker = (Player) ((EntityDamageByEntityEvent) event).getDamager(); + else if (((EntityDamageByEntityEvent) event).getDamager() instanceof Projectile) + if (((Projectile) ((EntityDamageByEntityEvent) event).getDamager()).getShooter() instanceof Player) + attacker = (Player) ((Projectile) ((EntityDamageByEntityEvent) event).getDamager()).getShooter(); + } + // Makes sure that if there was an attacking player, that the event is allowed for the game + if (attacker != null) { + // Cancel if one player is in the game but other isn't + if ((board.contains(player) && !board.contains(attacker)) || (!board.contains(player) && board.contains(attacker))) { + event.setCancelled(true); + return; + // Ignore event if neither player are in the game + } else if (!board.contains(player) && !board.contains(attacker)) { + return; + // Ignore event if players are on the same team, or one of them is a spectator + } else if (board.onSameTeam(player, attacker) || board.isSpectator(player) || board.isSpectator(attacker)) { + event.setCancelled(true); + return; + // Ignore the event if pvp is disabled, and a hider is trying to attack a seeker + } else if (!pvpEnabled && board.isHider(attacker) && board.isSeeker(player)) { + event.setCancelled(true); + return; + } + // If there was no attacker, if the damaged is not a player, ignore them. + } else if (!board.contains(player)) { + return; + // If there is no attacker, it most of been by natural causes. If pvp is disabled, and config doesn't allow natural causes, cancel event. + } else if (!pvpEnabled && !allowNaturalCauses && board.contains(player)) { + event.setCancelled(true); + return; + } + // Spectators and cannot take damage + if (board.isSpectator(player)) { + event.setCancelled(true); + if (Main.getInstance().supports(18) && player.getLocation().getBlockY() < -64) { + game.getCurrentMap().getGameSpawn().teleport(player); + } else if (!Main.getInstance().supports(18) && player.getLocation().getY() < 0) { + game.getCurrentMap().getGameSpawn().teleport(player); + } + return; + } + // Players cannot take damage while game is not in session + if (board.contains(player) && game.getStatus() != Status.PLAYING){ + event.setCancelled(true); + return; + } + // Check if player dies (pvp mode) + if(pvpEnabled && player.getHealth() - event.getFinalDamage() >= 0.5) return; + // Handle death event + event.setCancelled(true); + // Play death effect + if (Main.getInstance().supports(9)) { + XSound.ENTITY_PLAYER_DEATH.play(player, 1, 1); + } else { + XSound.ENTITY_PLAYER_HURT.play(player, 1, 1); + } + // Reveal player if they are disguised + Main.getInstance().getDisguiser().reveal(player); + // Teleport player to seeker spawn + if(delayedRespawn && !respawnAsSpectator){ + game.getCurrentMap().getGameSeekerLobby().teleport(player); + player.sendMessage(messagePrefix + message("RESPAWN_NOTICE").addAmount(delayedRespawnDelay)); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> { + if(game.getStatus() == Status.PLAYING){ + game.getCurrentMap().getGameSpawn().teleport(player); + } + }, delayedRespawnDelay * 20L); + } else { + game.getCurrentMap().getGameSpawn().teleport(player); + } + // Add leaderboard stats + board.addDeath(player.getUniqueId()); + if (attacker != null) board.addKill(attacker.getUniqueId()); + // Broadcast player death message + if (board.isSeeker(player)) { + game.broadcastMessage(message("GAME_PLAYER_DEATH").addPlayer(player).toString()); + } else if (board.isHider(player)) { + if (attacker == null) { + game.broadcastMessage(message("GAME_PLAYER_FOUND").addPlayer(player).toString()); + } else { + game.broadcastMessage(message("GAME_PLAYER_FOUND_BY").addPlayer(player).addPlayer(attacker).toString()); + } + if (respawnAsSpectator) { + board.addSpectator(player); + PlayerLoader.loadDeadHiderSpectator(player, game.getCurrentMap()); + } else { + board.addSeeker(player); + PlayerLoader.resetPlayer(player, board); + } + } + board.reloadBoardTeams(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerDeath(PlayerDeathEvent event){ + Main.getInstance().getDisguiser().reveal(event.getEntity()); + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/DisguiseHandler.java b/src/main/java/cat/freya/khs/game/listener/DisguiseHandler.java new file mode 100644 index 0000000..d874ae9 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/DisguiseHandler.java @@ -0,0 +1,106 @@ +package cat.freya.khs.game.listener; + +import static com.comphenix.protocol.PacketType.Play.Client.*; + +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.EnumWrappers; +import cat.freya.khs.Main; +import cat.freya.khs.game.util.Disguise; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import java.util.ArrayList; +import java.util.List; + +public class DisguiseHandler implements Listener { + + private static final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + public DisguiseHandler(){ + protocolManager.addPacketListener(createProtocol()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onMove(PlayerMoveEvent event) { + final Player player = event.getPlayer(); + final Disguise disguise = Main.getInstance().getDisguiser().getDisguise(player); + if(disguise == null) return;; + if(event.getFrom().distance(event.getTo()) > .1) { + disguise.setSolidify(false); + } + disguise.startSolidifying(); + } + + private PacketAdapter createProtocol(){ + return new PacketAdapter(Main.getInstance(), USE_ENTITY) { + + @Override + public void onPacketReceiving(PacketEvent event){ + PacketContainer packet = event.getPacket(); + + // only left click attacks + EnumWrappers.EntityUseAction action = packet.getEntityUseActions().getValues().stream().findFirst().orElse(null); + if (action == null) return; + //noinspection ComparatorResultComparison + if (action.compareTo(EnumWrappers.EntityUseAction.INTERACT) == 2) { + return; + } + + Player player = event.getPlayer(); + int id = packet.getIntegers().read(0); + Disguise disguise = Main.getInstance().getDisguiser().getByEntityID(id); + if(disguise == null) disguise = Main.getInstance().getDisguiser().getByHitBoxID(id); + if(disguise == null) return; + + if(disguise.getPlayer().getGameMode() == GameMode.CREATIVE) return; + event.setCancelled(true); + handleAttack(disguise, player); + } + }; + } + + private final List<Player> debounce = new ArrayList<>(); + + private void handleAttack(Disguise disguise, Player seeker){ + + if(disguise.getPlayer() == seeker) return; + + double amount; + if(Main.getInstance().supports(9)) { + amount = seeker.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE).getValue(); + } else { + return; //1.8 is not supported in Blockhunt yet!!! + } + + disguise.setSolidify(false); + if(debounce.contains(disguise.getPlayer())) return; + + debounce.add(disguise.getPlayer()); + + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> { + EntityDamageByEntityEvent event = + new EntityDamageByEntityEvent(seeker, disguise.getPlayer(), EntityDamageEvent.DamageCause.ENTITY_ATTACK, amount); + event.setDamage(amount); + disguise.getPlayer().setLastDamageCause(event); + Main.getInstance().getServer().getPluginManager().callEvent(event); + if(!event.isCancelled()){ + disguise.getPlayer().damage(amount); + disguise.getPlayer().setVelocity(seeker.getLocation().getDirection().setY(.2).multiply(1)); + } + + }, 0); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> debounce.remove(disguise.getPlayer()), 10); + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/InteractHandler.java b/src/main/java/cat/freya/khs/game/listener/InteractHandler.java new file mode 100644 index 0000000..09ab62b --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/InteractHandler.java @@ -0,0 +1,184 @@ +package cat.freya.khs.game.listener; + +import com.cryptomorin.xseries.XMaterial; +import com.cryptomorin.xseries.messages.ActionBar; +import cat.freya.khs.Main; +import cat.freya.khs.game.Board; +import cat.freya.khs.game.util.Status; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.ArrayList; +import java.util.List; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Config.glowPowerupItem; +import static cat.freya.khs.configuration.Localization.message; + +@SuppressWarnings("deprecation") +public class InteractHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerInteract(PlayerInteractEvent event) { + if (!Main.getInstance().getBoard().contains(event.getPlayer())) return; + if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock() != null && blockedInteracts.contains(event.getClickedBlock().getType().name())) { + event.setCancelled(true); + return; + } + ItemStack temp = event.getItem(); + if (temp == null) return; + if (Main.getInstance().getGame().getStatus() == Status.STANDBY) + onPlayerInteractLobby(temp, event); + if (Main.getInstance().getGame().getStatus() == Status.PLAYING) + onPlayerInteractGame(temp, event); + if (Main.getInstance().getBoard().isSpectator(event.getPlayer())) + onSpectatorInteract(temp, event); + } + + private void onPlayerInteractLobby(ItemStack temp, PlayerInteractEvent event) { + if (temp.isSimilar(lobbyLeaveItem)) { + event.setCancelled(true); + Main.getInstance().getGame().leave(event.getPlayer()); + } + + if (temp.isSimilar(lobbyStartItem) && event.getPlayer().hasPermission("hideandseek.start")) { + event.setCancelled(true); + if (Main.getInstance().getGame().checkCurrentMap()) { + event.getPlayer().sendMessage(errorPrefix + message("GAME_SETUP")); + return; + } + if (Main.getInstance().getGame().getStatus() != Status.STANDBY) { + event.getPlayer().sendMessage(errorPrefix + message("GAME_INPROGRESS")); + return; + } + if (Main.getInstance().getBoard().size() < minPlayers) { + event.getPlayer().sendMessage(errorPrefix + message("START_MIN_PLAYERS").addAmount(minPlayers)); + return; + } + Main.getInstance().getGame().start(); + } + } + + private void onPlayerInteractGame(ItemStack temp, PlayerInteractEvent event) { + if (temp.isSimilar(glowPowerupItem)) { + if (!glowEnabled) return; + Player player = event.getPlayer(); + if (Main.getInstance().getBoard().isHider(player)) { + Main.getInstance().getGame().getGlow().onProjectile(); + player.getInventory().remove(glowPowerupItem); + assert XMaterial.SNOWBALL.parseMaterial() != null; + player.getInventory().remove(XMaterial.SNOWBALL.parseMaterial()); + event.setCancelled(true); + } + } + } + + private void onSpectatorInteract(ItemStack temp, PlayerInteractEvent event){ + if(temp.isSimilar(flightToggleItem)){ + boolean isFlying = event.getPlayer().getAllowFlight(); + event.getPlayer().setAllowFlight(!isFlying); + event.getPlayer().setFlying(!isFlying); + ActionBar.clearActionBar(event.getPlayer()); + if(!isFlying){ + ActionBar.sendActionBar(event.getPlayer(), message("FLYING_ENABLED").toString()); + } else { + ActionBar.sendActionBar(event.getPlayer(), message("FLYING_DISABLED").toString()); + } + return; + } + if(temp.isSimilar(teleportItem)){ + // int amount = Main.getInstance().getBoard().getHiders().size() + Main.getInstance().getBoard().getSeekers().size(); + // Inventory teleportMenu = Main.getInstance().getServer().createInventory(null, 9*(((amount-1)/9)+1), ChatColor.stripColor(teleportItem.getItemMeta().getDisplayName())); + // List<String> hider_lore = new ArrayList<>(); hider_lore.add(message("HIDER_TEAM_NAME").toString()); + // Main.getInstance().getBoard().getHiders().forEach(hider -> teleportMenu.addItem(getSkull(hider, hider_lore))); + // List<String> seeker_lore = new ArrayList<>(); seeker_lore.add(message("SEEKER_TEAM_NAME").toString()); + // Main.getInstance().getBoard().getSeekers().forEach(seeker -> teleportMenu.addItem(getSkull(seeker, seeker_lore))); + // event.getPlayer().openInventory(teleportMenu); + createSpectatorTeleportPage(event.getPlayer(), 0); + } + } + + public static void createSpectatorTeleportPage(Player player, int page) { + + if (page < 0) { + return; + } + + final Board board = Main.getInstance().getBoard(); + List<Player> players = new ArrayList<>(); + players.addAll(board.getHiders()); + players.addAll(board.getSeekers()); + + final int page_size = 9 * 5; + final int amount = players.size(); + final int start = page * page_size; + + int page_amount = amount - start; + + if (page_amount < 1) { + return; + } + + boolean next = false, prev = true; + + if (page_amount > page_size) { + page_amount = page_size; + next = true; + } + + if (page == 0) { + prev = false; + } + + final int rows = ((amount - 1) / 9) + 2; + + final Inventory teleportMenu = Main.getInstance().getServer().createInventory(null, 9 * rows, ChatColor.stripColor(teleportItem.getItemMeta().getDisplayName())); + + final List<String> hider_lore = new ArrayList<>(); hider_lore.add(message("HIDER_TEAM_NAME").toString()); + final List<String> seeker_lore = new ArrayList<>(); seeker_lore.add(message("SEEKER_TEAM_NAME").toString()); + + for (int i = 0; i < page_amount; i++) { + Player plr = players.get(i); + teleportMenu.addItem(getSkull(plr, board.isHider(plr) ? hider_lore : seeker_lore)); + } + + final int lastRow = (rows - 1) * 9; + if (prev) { + teleportMenu.setItem(lastRow, getPageItem(page - 1)); + } + + if (next) { + teleportMenu.setItem(lastRow + 8, getPageItem(page + 1)); + } + + player.openInventory(teleportMenu); + } + + private static ItemStack getPageItem(int page) { + ItemStack prevItem = new ItemStack(XMaterial.ENCHANTED_BOOK.parseMaterial(), page + 1); + ItemMeta meta = prevItem.getItemMeta(); + meta.setDisplayName("Page " + (page+1)); + prevItem.setItemMeta(meta); + return prevItem; + } + + private static ItemStack getSkull(Player player, List<String> lore){ + assert XMaterial.PLAYER_HEAD.parseMaterial() != null; + ItemStack playerHead = new ItemStack(XMaterial.PLAYER_HEAD.parseMaterial(), 1, (byte) 3); + SkullMeta playerHeadMeta = (SkullMeta) playerHead.getItemMeta(); + playerHeadMeta.setOwner(player.getName()); + playerHeadMeta.setDisplayName(player.getName()); + playerHeadMeta.setLore(lore); + playerHead.setItemMeta(playerHeadMeta); + return playerHead; + } +} diff --git a/src/main/java/cat/freya/khs/game/listener/InventoryHandler.java b/src/main/java/cat/freya/khs/game/listener/InventoryHandler.java new file mode 100644 index 0000000..ccf292d --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/InventoryHandler.java @@ -0,0 +1,147 @@ +/* + * This file is part of Kenshins Hide and Seek + * + * Copyright (c) 2022 Tyler Murphy. + * + * Kenshins Hide and Seek free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * he Free Software Foundation version 3. + * + * Kenshins Hide and Seek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package cat.freya.khs.game.listener; + +import com.cryptomorin.xseries.XMaterial; +import cat.freya.khs.Main; +import cat.freya.khs.command.map.Debug; +import cat.freya.khs.configuration.Map; +import cat.freya.khs.configuration.Maps; +import cat.freya.khs.game.util.Status; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class InventoryHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryClick(InventoryClickEvent event) { + if (event.getCurrentItem() == null) return; + if (!(event.getWhoClicked() instanceof Player)) return; + checkForInventoryMove(event); + checkForSpectatorTeleportMenu(event); + checkForDebugMenu(event); + checkForBlockHuntMenu(event); + } + + private void checkForInventoryMove(InventoryClickEvent event){ + if (Main.getInstance().getBoard().contains((Player) event.getWhoClicked()) && Main.getInstance().getGame().getStatus() == Status.STANDBY) { + event.setCancelled(true); + } + } + + private void checkForSpectatorTeleportMenu(InventoryClickEvent event){ + Player player = (Player) event.getWhoClicked(); + + ItemStack item = event.getCurrentItem(); + + ItemMeta meta = item.getItemMeta(); + String name = meta.getDisplayName(); + + if (Main.getInstance().getBoard().isSpectator(player)) { + if (XMaterial.PLAYER_HEAD.isSimilar(item)) { + event.setCancelled(true); + player.closeInventory(); + Player clicked = Main.getInstance().getServer().getPlayer(name); + if (clicked == null) return; + player.teleport(clicked); + } else if (XMaterial.ENCHANTED_BOOK.isSimilar(item)) { + event.setCancelled(true); + player.closeInventory(); + if (!name.startsWith("Page ")) return; + String number_str = name.substring(5); + try { + int page = Integer.parseInt(number_str); + InteractHandler.createSpectatorTeleportPage(player, page - 1); + } catch(Exception ignored) { + return; + } + } + } + } + + private void checkForDebugMenu(InventoryClickEvent event){ + Player player = (Player) event.getWhoClicked(); + boolean debug; + if(Main.getInstance().supports(14)){ + debug = event.getView().getTitle().equals("Debug Menu") && player.hasPermission("hideandseek.debug"); + } else { + debug = event.getInventory().getName().equals("Debug Menu") && player.hasPermission("hideandseek.debug"); + } + if (debug){ + event.setCancelled(true); + player.closeInventory(); + Debug.handleOption(player, event.getRawSlot()); + } + } + + private void checkForBlockHuntMenu(InventoryClickEvent event){ + boolean test; + String mapName; + if(Main.getInstance().supports(14)){ + test = event.getView().getTitle().startsWith("Select a Block: "); + if(!test) return; + mapName = event.getView().getTitle().substring("Select a Block: ".length()); + } else { + test = event.getInventory().getName().startsWith("Select a Block: "); + if(!test) return; + mapName = event.getInventory().getName().substring("Select a Block: ".length()); + } + event.setCancelled(true); + Map map = Maps.getMap(mapName); + if(map == null) return; + Material mat = map.getBlockHunt().get(event.getRawSlot()); + if(mat == null) return; + Player player = (Player) event.getWhoClicked(); + Main.getInstance().getDisguiser().disguise(player, mat, map); + player.closeInventory(); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryClose(InventoryCloseEvent event){ + if (!(event.getPlayer() instanceof Player)) return; + boolean test; + String mapName; + if(Main.getInstance().supports(14)){ + test = event.getView().getTitle().startsWith("Select a Block: "); + if(!test) return; + mapName = event.getView().getTitle().substring("Select a Block: ".length()); + } else { + test = event.getInventory().getName().startsWith("Select a Block: "); + if(!test) return; + mapName = event.getInventory().getName().substring("Select a Block: ".length()); + } + Map map = Maps.getMap(mapName); + if(map == null) return; + Material mat = map.getBlockHunt().get(0); + if(mat == null) return; + Player player = (Player) event.getPlayer(); + if(Main.getInstance().getDisguiser().disguised(player)) return; + Main.getInstance().getDisguiser().disguise(player, mat, map); + player.closeInventory(); + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/JoinLeaveHandler.java b/src/main/java/cat/freya/khs/game/listener/JoinLeaveHandler.java new file mode 100644 index 0000000..ba83fa4 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/JoinLeaveHandler.java @@ -0,0 +1,98 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Items; +import cat.freya.khs.game.PlayerLoader; +import cat.freya.khs.game.util.Status; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import static cat.freya.khs.configuration.Config.*; +import static cat.freya.khs.configuration.Localization.message; + +public class JoinLeaveHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerJoin(PlayerJoinEvent event) { + if(!Main.getInstance().getDatabase().getNameData().update(event.getPlayer().getUniqueId(), event.getPlayer().getName())){ + Main.getInstance().getLogger().warning("Failed to save name data for user: " + event.getPlayer().getName()); + } + Main.getInstance().getBoard().remove(event.getPlayer()); + removeItems(event.getPlayer()); + if (Main.getInstance().getGame().checkCurrentMap()) return; + if (autoJoin) { + if (Main.getInstance().getGame().checkCurrentMap()) { + event.getPlayer().sendMessage(errorPrefix + message("GAME_SETUP")); + return; + } + Main.getInstance().getGame().join(event.getPlayer()); + } else if (teleportToExit) { + if ( + event.getPlayer().getWorld().getName().equals(Main.getInstance().getGame().getCurrentMap().getLobbyName()) || + event.getPlayer().getWorld().getName().equals(Main.getInstance().getGame().getCurrentMap().getGameSpawnName()) + ) { + exitPosition.teleport(event.getPlayer()); + event.getPlayer().setGameMode(GameMode.ADVENTURE); + } + } else { + if (mapSaveEnabled && event.getPlayer().getWorld().getName().equals(Main.getInstance().getGame().getCurrentMap().getGameSpawnName())) { + if (Main.getInstance().getGame().getStatus() != Status.STANDBY && Main.getInstance().getGame().getStatus() != Status.ENDING) { + Main.getInstance().getGame().join(event.getPlayer()); + } else { + exitPosition.teleport(event.getPlayer()); + event.getPlayer().setGameMode(GameMode.ADVENTURE); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onQuit(PlayerQuitEvent event) { + handleLeave(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onKick(PlayerKickEvent event) { + if(event.getReason().equals("Flying is not enabled on this server!")){ + event.setCancelled(true); + return; + } + handleLeave(event.getPlayer()); + } + + private void handleLeave(Player player) { + if(!Main.getInstance().getBoard().contains(player)) return; + PlayerLoader.unloadPlayer(player); + Main.getInstance().getBoard().remove(player); + if(saveInventory) { + ItemStack[] data = Main.getInstance().getDatabase().getInventoryData().getInventory(player.getUniqueId()); + player.getInventory().setContents(data); + } + if (Main.getInstance().getGame().getStatus() == Status.STANDBY) { + Main.getInstance().getBoard().reloadLobbyBoards(); + } else { + Main.getInstance().getBoard().reloadGameBoards(); + } + } + + private void removeItems(Player player) { + for(ItemStack si : Items.SEEKER_ITEMS) { + if (si == null) continue; + for (ItemStack i : player.getInventory().getContents()) + if (si.isSimilar(i)) player.getInventory().remove(i); + } + for(ItemStack hi : Items.HIDER_ITEMS) { + if (hi == null) continue; + for (ItemStack i : player.getInventory().getContents()) + if (hi.isSimilar(i)) player.getInventory().remove(i); + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/MovementHandler.java b/src/main/java/cat/freya/khs/game/listener/MovementHandler.java new file mode 100644 index 0000000..b6007a8 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/MovementHandler.java @@ -0,0 +1,61 @@ +package cat.freya.khs.game.listener; + +import com.google.common.collect.Sets; +import cat.freya.khs.game.listener.events.PlayerJumpEvent; +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Map; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +import java.util.Set; +import java.util.UUID; + +public class MovementHandler implements Listener { + + private final Set<UUID> prevPlayersOnGround = Sets.newHashSet(); + + @EventHandler(priority = EventPriority.HIGHEST) + public void onMove(PlayerMoveEvent event) { + + if (event.getTo() == null || event.getTo().getWorld() == null) return; + checkJumping(event); + checkBounds(event); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onJump(PlayerJumpEvent event) { + if(Main.getInstance().getBoard().isSpectator(event.getPlayer()) && event.getPlayer().getAllowFlight()) { + event.getPlayer().setFlying(true); + } + } + + private void checkJumping(PlayerMoveEvent event){ + if (event.getPlayer().getVelocity().getY() > 0) { + if (event.getPlayer().getLocation().getBlock().getType() != Material.LADDER && prevPlayersOnGround.contains(event.getPlayer().getUniqueId())) { + if (!event.getPlayer().isOnGround()) { + Main.getInstance().getServer().getPluginManager().callEvent(new PlayerJumpEvent(event.getPlayer())); + } + } + } + if (event.getPlayer().isOnGround()) { + prevPlayersOnGround.add(event.getPlayer().getUniqueId()); + } else { + prevPlayersOnGround.remove(event.getPlayer().getUniqueId()); + } + } + + private void checkBounds(PlayerMoveEvent event){ + if (!Main.getInstance().getBoard().contains(event.getPlayer())) return; + if (!event.getPlayer().getWorld().getName().equals(Main.getInstance().getGame().getCurrentMap().getGameSpawnName())) return; + if (!event.getTo().getWorld().getName().equals(Main.getInstance().getGame().getCurrentMap().getGameSpawnName())) return; + if (event.getPlayer().hasPermission("hs.leavebounds")) return; + Map map = Main.getInstance().getGame().getCurrentMap(); + if (event.getTo().getBlockX() < map.getBoundsMin().getBlockX() || event.getTo().getBlockX() > map.getBoundsMax().getBlockX() || event.getTo().getBlockZ() < map.getBoundsMin().getZ() || event.getTo().getBlockZ() > map.getBoundsMax().getZ()) { + event.setCancelled(true); + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/PlayerHandler.java b/src/main/java/cat/freya/khs/game/listener/PlayerHandler.java new file mode 100644 index 0000000..5c60536 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/PlayerHandler.java @@ -0,0 +1,56 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import cat.freya.khs.configuration.Items; +import cat.freya.khs.game.util.Status; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.inventory.ItemStack; + +import static cat.freya.khs.configuration.Config.dropItems; +import static cat.freya.khs.configuration.Config.regenHealth; + +public class PlayerHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onFoodLevelChange(FoodLevelChangeEvent event) { + if (event.getEntity() instanceof Player) { + if (!Main.getInstance().getBoard().contains((Player) event.getEntity())) return; + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerRegainHealth(EntityRegainHealthEvent event) { + if (regenHealth) return; + if (event.getRegainReason() == EntityRegainHealthEvent.RegainReason.SATIATED || event.getRegainReason() == EntityRegainHealthEvent.RegainReason.REGEN) { + if (event.getEntity() instanceof Player) { + if (!Main.getInstance().getBoard().contains((Player) event.getEntity())) return; + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onItemDrop(PlayerDropItemEvent event) { + if (!dropItems && Main.getInstance().getBoard().contains(event.getPlayer())) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onItemSpawn(ItemSpawnEvent event){ + if(Main.getInstance().getGame().getStatus() == Status.STANDBY) return; + ItemStack item = event.getEntity().getItemStack(); + if (!Items.matchItem(item)) return; + if (dropItems) return; + event.setCancelled(true); + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/RespawnHandler.java b/src/main/java/cat/freya/khs/game/listener/RespawnHandler.java new file mode 100644 index 0000000..63aaab6 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/RespawnHandler.java @@ -0,0 +1,40 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerRespawnEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class RespawnHandler implements Listener { + + public static final Map<UUID, Location> temp_loc = new HashMap<>(); + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + if (!Main.getInstance().getBoard().contains(player)) return; + event.setKeepInventory(true); + event.setDeathMessage(""); + temp_loc.put(player.getUniqueId(), player.getLocation()); + Main.getInstance().getLogger().severe("Player " + player.getName() + " died when not supposed to. Attempting to roll back death."); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerRespawn(PlayerRespawnEvent event) { + Player player = event.getPlayer(); + if (!Main.getInstance().getBoard().contains(player)) return; + if (temp_loc.containsKey(player.getUniqueId())) { + player.teleport(temp_loc.get(player.getUniqueId())); + temp_loc.remove(player.getUniqueId()); + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/WorldInteractHandler.java b/src/main/java/cat/freya/khs/game/listener/WorldInteractHandler.java new file mode 100644 index 0000000..883d963 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/WorldInteractHandler.java @@ -0,0 +1,48 @@ +package cat.freya.khs.game.listener; + +import cat.freya.khs.Main; +import cat.freya.khs.game.Board; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityBreakDoorEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; + +public class WorldInteractHandler implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + Board board = Main.getInstance().getBoard(); + if(board.contains(player)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onHangingEntityBreakByEntity(HangingBreakByEntityEvent event) { + if (!(event.getRemover() instanceof Player)) { + return; + } + Player player = (Player) event.getRemover(); + Board board = Main.getInstance().getBoard(); + if(board.contains(player)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBreakDoor(EntityBreakDoorEvent event) { + if (!(event.getEntity() instanceof Player)) { + return; + } + Player player = (Player) event.getEntity(); + Board board = Main.getInstance().getBoard(); + if(board.contains(player)) { + event.setCancelled(true); + } + } + +} diff --git a/src/main/java/cat/freya/khs/game/listener/events/PlayerJumpEvent.java b/src/main/java/cat/freya/khs/game/listener/events/PlayerJumpEvent.java new file mode 100644 index 0000000..b3cd0ad --- /dev/null +++ b/src/main/java/cat/freya/khs/game/listener/events/PlayerJumpEvent.java @@ -0,0 +1,56 @@ +/* + * This file is part of Kenshins Hide and Seek + * + * Copyright (c) 2022 Tyler Murphy. + * + * Kenshins Hide and Seek free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * he Free Software Foundation version 3. + * + * Kenshins Hide and Seek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package cat.freya.khs.game.listener.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; + +public class PlayerJumpEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + + public PlayerJumpEvent(Player player) { + super(player); + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean b) { + this.cancel = !b; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + @SuppressWarnings("unused") + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/cat/freya/khs/game/util/CountdownDisplay.java b/src/main/java/cat/freya/khs/game/util/CountdownDisplay.java new file mode 100644 index 0000000..05548d4 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/util/CountdownDisplay.java @@ -0,0 +1,26 @@ +/* + * This file is part of Kenshins Hide and Seek + * + * Copyright (c) 2022 Tyler Murphy. + * + * Kenshins Hide and Seek free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * he Free Software Foundation version 3. + * + * Kenshins Hide and Seek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package cat.freya.khs.game.util; + +public enum CountdownDisplay { + CHAT, + ACTIONBAR, + TITLE +} diff --git a/src/main/java/cat/freya/khs/game/util/Disguise.java b/src/main/java/cat/freya/khs/game/util/Disguise.java new file mode 100644 index 0000000..56b1283 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/util/Disguise.java @@ -0,0 +1,219 @@ +package cat.freya.khs.game.util; + +import com.cryptomorin.xseries.XSound; +import com.cryptomorin.xseries.messages.ActionBar; +import cat.freya.khs.util.packet.BlockChangePacket; +import cat.freya.khs.util.packet.EntityTeleportPacket; +import cat.freya.khs.Main; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.*; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +@SuppressWarnings("deprecation") +public class Disguise { + + final Player hider; + final Material material; + FallingBlock block; + AbstractHorse hitBox; + Location blockLocation; + boolean solid, solidify, solidifying; + static Team hidden; + + static { + if(Main.getInstance().supports(9)) { + Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard(); + hidden = board.getTeam("KHS_Collision"); + if (hidden == null) { + hidden = board.registerNewTeam("KHS_Collision"); + } + hidden.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); + hidden.setCanSeeFriendlyInvisibles(false); + } + } + + public Disguise(Player player, Material material){ + this.hider = player; + this.material = material; + this.solid = false; + respawnFallingBlock(); + player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + if(Main.getInstance().supports(9)) { + hidden.addEntry(player.getName()); + } else { + hider.spigot().setCollidesWithEntities(false); + } + } + + public void remove(){ + if(block != null) + block.remove(); + if(hitBox != null){ + if(Main.getInstance().supports(9)) + hidden.removeEntry(hitBox.getUniqueId().toString()); + hitBox.remove(); + } + if(solid) + sendBlockUpdate(blockLocation, Material.AIR); + hider.removePotionEffect(PotionEffectType.INVISIBILITY); + if(Main.getInstance().supports(9)) { + hidden.removeEntry(hider.getName()); + } else { + hider.spigot().setCollidesWithEntities(true); + } + } + + public int getEntityID() { + if(block == null) return -1; + return block.getEntityId(); + } + + public int getHitBoxID() { + if(hitBox == null) return -1; + return hitBox.getEntityId(); + } + + public Player getPlayer() { + return hider; + } + + public void update(){ + + if(block == null || block.isDead()){ + if(block != null) block.remove(); + respawnFallingBlock(); + } + + if(solidify){ + if(!solid) { + solid = true; + blockLocation = hider.getLocation().getBlock().getLocation(); + respawnHitbox(); + } + sendBlockUpdate(blockLocation, material); + } else if(solid){ + solid = false; + if(Main.getInstance().supports(9)) + hidden.removeEntry(hitBox.getUniqueId().toString()); + hitBox.remove(); + hitBox = null; + sendBlockUpdate(blockLocation, Material.AIR); + } + toggleEntityVisibility(block, !solid); + teleportEntity(hitBox, true); + teleportEntity(block, solid); + } + + public void setSolidify(boolean value){ + this.solidify = value; + } + + private void sendBlockUpdate(Location location, Material material){ + BlockChangePacket packet = new BlockChangePacket(); + packet.setBlockPosition(location); + packet.setMaterial(material); + Bukkit.getOnlinePlayers().forEach(receiver -> { + if(receiver.getName().equals(hider.getName())) return; + packet.send(receiver); + }); + } + + private void teleportEntity(Entity entity, boolean center) { + if(entity == null) return; + EntityTeleportPacket packet = new EntityTeleportPacket(); + packet.setEntity(entity); + double x,y,z; + if(center){ + x = Math.round(hider.getLocation().getX()+.5)-.5; + y = Math.round(hider.getLocation().getY()); + z = Math.round(hider.getLocation().getZ()+.5)-.5; + } else { + x = hider.getLocation().getX(); + y = hider.getLocation().getY(); + z = hider.getLocation().getZ(); + } + packet.setX(x); + packet.setY(y); + packet.setZ(z); + Bukkit.getOnlinePlayers().forEach(packet::send); + } + + private void toggleEntityVisibility(Entity entity, boolean show){ + if(entity == null) return; + Bukkit.getOnlinePlayers().forEach(receiver -> { + if(receiver == hider) return; + if(show) + Main.getInstance().getEntityHider().showEntity(receiver, entity); + else + Main.getInstance().getEntityHider().hideEntity(receiver, entity); + }); + } + + private void respawnFallingBlock(){ + block = hider.getLocation().getWorld().spawnFallingBlock(hider.getLocation().add(0, 1000, 0), material, (byte)0); + if (Main.getInstance().supports(10)) { + block.setGravity(false); + } + block.setDropItem(false); + block.setInvulnerable(true); + } + + private void respawnHitbox(){ + if (Main.getInstance().supports(11)) { + hitBox = (AbstractHorse) hider.getLocation().getWorld().spawnEntity(hider.getLocation().add(0, 1000, 0), EntityType.SKELETON_HORSE); + } else { + hitBox = (AbstractHorse) hider.getLocation().getWorld().spawnEntity(hider.getLocation().add(0, 1000, 0), EntityType.HORSE); + hitBox.setVariant(Horse.Variant.SKELETON_HORSE); + } + if (Main.getInstance().supports(10)) { + hitBox.setGravity(false); + } + hitBox.setAI(false); + hitBox.setInvulnerable(true); + hitBox.setCanPickupItems(false); + hitBox.setCollidable(false); + hitBox.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + if(Main.getInstance().supports(9)){ + hidden.addEntry(hitBox.getUniqueId().toString()); + } + } + + public void startSolidifying() { + if (solidifying) return; + if (solid) return; + solidifying = true; + final Location lastLocation = hider.getLocation(); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> solidifyUpdate(lastLocation, 3), 10); + } + + private void solidifyUpdate(Location lastLocation, int time) { + Location currentLocation = hider.getLocation(); + if(lastLocation.getWorld() != currentLocation.getWorld()) { + solidifying = false; + return; + } + if(lastLocation.distance(currentLocation) > .1) { + solidifying = false; + return; + } + if(time == 0) { + ActionBar.clearActionBar(hider); + setSolidify(true); + solidifying = false; + } else { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < time; i++) { + s.append("▪"); + } + ActionBar.sendActionBar(hider, s.toString()); + XSound.BLOCK_NOTE_BLOCK_PLING.play(hider, 1, 1); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> solidifyUpdate(lastLocation, time - 1), 20); + } + } + +}
\ No newline at end of file diff --git a/src/main/java/cat/freya/khs/game/util/Status.java b/src/main/java/cat/freya/khs/game/util/Status.java new file mode 100644 index 0000000..c38b9ee --- /dev/null +++ b/src/main/java/cat/freya/khs/game/util/Status.java @@ -0,0 +1,7 @@ +package cat.freya.khs.game.util; + +public enum Status { + + STANDBY, STARTING, PLAYING, ENDING, ENDED + +} diff --git a/src/main/java/cat/freya/khs/game/util/WinType.java b/src/main/java/cat/freya/khs/game/util/WinType.java new file mode 100644 index 0000000..38a6920 --- /dev/null +++ b/src/main/java/cat/freya/khs/game/util/WinType.java @@ -0,0 +1,7 @@ +package cat.freya.khs.game.util; + +public enum WinType { + + HIDER_WIN, SEEKER_WIN, NONE + +} |