diff --git a/README.md b/README.md index 507a38b..b692ea7 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,8 @@ These are all the options that can be changed for an arena. | winLocation | The location players must reach to win the arena (see spawnLocation for valid values). If set, this overrides, and is used instead of, the win block type. | | checkpointAdd | Adds a new checkpoint to the arena's checkpoints (see spawnLocation for valid values). | | checkpointClear | Clears all current checkpoints. Give any value to execute. If not given a value, current checkpoints are shown. | -| killPlaneBlocks | A comma-separated list of materials which will force a loss on hit. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. | -| horizontalKillPlaneHitBox | The number of blocks away kill plane blocks will trigger horizontally (0-1). This only affects things less than 1 block wide, such as horizontal lightning and end rods. | +| killPlaneBlocks | A comma-separated list of materials which will force a loss when stepped on. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. | +| obstacleBlocks | A comma-separated list of materials which will force a loss when touched from any direction. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. | ## Configuration options @@ -249,6 +249,7 @@ These are all the options that can be changed for an arena. | mustDoGroupedInSequence | true/false | true | Whether grouped dropper arenas must be played in the correct sequence | | ignoreRecordsUntilGroupBeatenOnce | true/false | false | Whether records won't be registered unless the player has already beaten all arenas in a group. That means players are required to do a second play-through to register a record for a grouped arena. | | killPlaneBlocks | list | [see this](#killplaneblocks-default) | The types of blocks compromising parkour arenas' kill planes. Add any materials you want to use for the "bottom" of your parkour arenas. +WOOL and other [material tags](#notes-about-material-tags) are supported. | +| obstacleBlocks | list | [see this](#obstacleblocks-default) | The types of blocks treated as obstacles in every direction. +WOOL and other [material tags](#notes-about-material-tags) are supported. | #### blockWhitelist default: @@ -270,6 +271,12 @@ These are all the options that can be changed for an arena. - LAVA - MAGMA_BLOCK +#### obstacleBlocks default: + +- END_ROD +- LIGHTNING_ROD +- CHAIN + ## Record placeholders Player records can be displayed on a leaderboard by using PlaceholderAPI. If you want to display a sign-based diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java index b6e9ed5..97cf974 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java @@ -74,9 +74,14 @@ public class ParkourArena implements Arena { private @Nullable Set killPlaneBlocks; /** - * The number of horizontal blocks the hit-box of kill plane blocks should cover + * The names of the block types serving as obstacles for this arena */ - private double horizontalKillPlaneHitBox; + private @Nullable Set obstacleBlockNames; + + /** + * The block types serving as obstacles for this arena + */ + private @Nullable Set obstacleBlocks; /** * The checkpoints for this arena. Entering a checkpoint overrides the player's spawn location. @@ -95,22 +100,22 @@ public class ParkourArena implements Arena { /** * Instantiates a new parkour arena * - * @param arenaId

The id of the arena

- * @param arenaName

The name of the arena

- * @param spawnLocation

The location players spawn in when entering the arena

- * @param exitLocation

The location the players are teleported to when exiting the arena, or null

- * @param winBlockType

The material of the block players have to hit to win this parkour arena

- * @param winLocation

The location a player has to reach to win this arena

- * @param killPlaneBlockNames

The names of the type of blocks

- * @param horizontalKillPlaneHitBox

The number of horizontal blocks the hit-box of kill plane blocks should cover

- * @param checkpoints

The checkpoints set for this arena

- * @param rewards

The rewards given by this arena

- * @param parkourArenaData

The arena data keeping track of which players have done what in this arena

- * @param arenaHandler

The arena handler used for saving any changes

+ * @param arenaId

The id of the arena

+ * @param arenaName

The name of the arena

+ * @param spawnLocation

The location players spawn in when entering the arena

+ * @param exitLocation

The location the players are teleported to when exiting the arena, or null

+ * @param winBlockType

The material of the block players have to hit to win this parkour arena

+ * @param winLocation

The location a player has to reach to win this arena

+ * @param killPlaneBlockNames

The names of the types of blocks that trigger a loss when stepped on

+ * @param obstacleBlockNames

The names of the types of blocks that trigger a loss when touched

+ * @param checkpoints

The checkpoints set for this arena

+ * @param rewards

The rewards given by this arena

+ * @param parkourArenaData

The arena data keeping track of which players have done what in this arena

+ * @param arenaHandler

The arena handler used for saving any changes

*/ public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation, @Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location winLocation, - @Nullable Set killPlaneBlockNames, double horizontalKillPlaneHitBox, + @Nullable Set killPlaneBlockNames, @Nullable Set obstacleBlockNames, @NotNull List checkpoints, @NotNull Map> rewards, @NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) { @@ -123,7 +128,9 @@ public class ParkourArena implements Arena { this.killPlaneBlockNames = killPlaneBlockNames; this.killPlaneBlocks = this.killPlaneBlockNames == null ? null : MaterialHelper.loadMaterialList( new ArrayList<>(killPlaneBlockNames), "+", MiniGames.getInstance().getLogger()); - this.horizontalKillPlaneHitBox = horizontalKillPlaneHitBox; + this.obstacleBlockNames = obstacleBlockNames; + this.obstacleBlocks = this.obstacleBlockNames == null ? null : MaterialHelper.loadMaterialList( + new ArrayList<>(obstacleBlockNames), "+", MiniGames.getInstance().getLogger()); this.checkpoints = checkpoints; this.parkourArenaData = parkourArenaData; this.parkourArenaHandler = arenaHandler; @@ -156,9 +163,9 @@ public class ParkourArena implements Arena { this.parkourArenaData = new ParkourArenaData(this.arenaId, recordRegistries, new HashMap<>()); this.winBlockType = Material.EMERALD_BLOCK; this.killPlaneBlocks = null; + this.obstacleBlocks = null; this.checkpoints = new ArrayList<>(); this.parkourArenaHandler = arenaHandler; - this.horizontalKillPlaneHitBox = 0.1; } @Override @@ -251,34 +258,25 @@ public class ParkourArena implements Arena { } /** - * Gets the number of horizontal blocks the hit-box of kill plane blocks should cover + * Gets the block types used for this parkour arena's obstacle blocks * - *

This is kind of hard to explain, but basically, when the player is less than the specified amount of blocks - * away from a kill plane block horizontally, a fail will be triggered. Sane values for this would usually be - * between 0.01 amd 1.

- * - * @return

The number of horizontal blocks the hit-box of kill plane blocks should cover

+ * @return

The types of blocks used as obstacles

*/ - public double getHorizontalKillPlaneHitBox() { - return this.horizontalKillPlaneHitBox; + public @NotNull Set getObstacleBlocks() { + if (this.obstacleBlocks != null) { + return new HashSet<>(this.obstacleBlocks); + } else { + return MiniGames.getInstance().getParkourConfiguration().getObstacleBlocks(); + } } /** - * Sets the number of horizontal blocks the hit-box of kill plane blocks should cover + * Gets the names of the blocks used as this arena's obstacle blocks * - *

This is kind of hard to explain, but basically, when the player is less than the specified amount of blocks - * away from a kill plane block horizontally, a fail will be triggered. Sane values for this would usually be - * between 0.01 amd 1.

- * - * @param horizontalKillPlaneHitBox

The number of horizontal blocks the hit-box of kill plane blocks should cover

+ * @return

The names of the blocks used as this arena's obstacle blocks

*/ - public boolean setHorizontalKillPlaneHitBox(double horizontalKillPlaneHitBox) { - if (horizontalKillPlaneHitBox > 1 || horizontalKillPlaneHitBox < -1) { - return false; - } - this.horizontalKillPlaneHitBox = horizontalKillPlaneHitBox; - this.saveArena(); - return true; + public @Nullable Set getObstacleBlockNames() { + return this.obstacleBlockNames; } /** @@ -335,7 +333,7 @@ public class ParkourArena implements Arena { @Override public boolean willCauseLoss(Block block) { - return this.getKillPlaneBlocks().contains(block.getType()); + return this.getKillPlaneBlocks().contains(block.getType()) || this.getObstacleBlocks().contains(block.getType()); } @Override @@ -451,6 +449,28 @@ public class ParkourArena implements Arena { return true; } + /** + * Sets the type of blocks used as obstacle blocks + * + * @param obstacleBlockNames

The names of the obstacle blocks

+ */ + public boolean setObstacleBlocks(@NotNull Set obstacleBlockNames) { + if (obstacleBlockNames.isEmpty()) { + this.obstacleBlockNames = null; + this.obstacleBlocks = null; + } else { + Set parsed = MaterialHelper.loadMaterialList(new ArrayList<>(obstacleBlockNames), "+", + MiniGames.getInstance().getLogger()); + if (parsed.isEmpty()) { + return false; + } + this.obstacleBlockNames = obstacleBlockNames; + this.obstacleBlocks = parsed; + } + this.saveArena(); + return true; + } + /** * Adds a checkpoint to this arena * diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java index 2a20438..247727e 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java @@ -64,10 +64,12 @@ public enum ParkourArenaEditableProperty { EditablePropertyType.MATERIAL_LIST), /** - * The horizontal hit-box of kill blocks + * The blocks used as this arena's obstacle blocks */ - HORIZONTAL_KILL_PLANE_HIT_BOX("horizontalKillPlaneHitBox", - (arena) -> String.valueOf(arena.getHorizontalKillPlaneHitBox()), EditablePropertyType.DOUBLE); + OBSTACLE_BLOCKS("obstacleBlocks", (arena) -> String.valueOf(arena.getObstacleBlockNames()), + EditablePropertyType.MATERIAL_LIST), + + ; private final @NotNull String argumentString; private final Function currentValueProvider; diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java index 7b9da13..9923ebf 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java @@ -43,9 +43,9 @@ public enum ParkourArenaStorageKey { KILL_PLANE_BLOCKS("killPlaneBlocks"), /** - * The key for this arena's horizontal kill plane hit box + * The key for this arena's obstacle blocks (overrides the config) */ - HORIZONTAL_KILL_PLANE_HIT_BOX("horizontalKillPlaneHitBox"), + OBSTACLE_BLOCKS("obstacleBlocks"), /** * The key for this arena's checkpoint locations diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java index 238dbac..7762c06 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java @@ -98,7 +98,7 @@ public class EditParkourArenaCommand implements CommandExecutor { case CHECKPOINT_ADD -> arena.addCheckpoint(parseLocation(player, value)); case CHECKPOINT_CLEAR -> arena.clearCheckpoints(); case KILL_PLANE_BLOCKS -> arena.setKillPlaneBlocks(new HashSet<>(List.of(value.split(",")))); - case HORIZONTAL_KILL_PLANE_HIT_BOX -> arena.setHorizontalKillPlaneHitBox(Double.parseDouble(value)); + case OBSTACLE_BLOCKS -> arena.setObstacleBlocks(new HashSet<>(List.of(value.split(",")))); }; } diff --git a/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java b/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java index c0f3848..28ab97f 100644 --- a/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java +++ b/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java @@ -17,6 +17,7 @@ public class ParkourConfiguration extends MiniGameConfiguration { private boolean mustDoGroupedInSequence; private boolean ignoreRecordsUntilGroupBeatenOnce; private Set killPlaneBlocks; + private Set obstacleBlocks; /** * Instantiates a new dropper configuration @@ -63,12 +64,22 @@ public class ParkourConfiguration extends MiniGameConfiguration { return new HashSet<>(this.killPlaneBlocks); } + /** + * Gets all types of blocks constituting parkour arena's obstacle blocks + * + * @return

The types of blocks constituting parkour arena's obstacle blocks

+ */ + public Set getObstacleBlocks() { + return new HashSet<>(this.obstacleBlocks); + } + @Override protected void load() { this.enforceCheckpointOrder = configuration.getBoolean(rootNode + "enforceCheckpointOrder", false); this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true); this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false); this.killPlaneBlocks = loadMaterialList(rootNode + "killPlaneBlocks"); + this.obstacleBlocks = loadMaterialList(rootNode + "obstacleBlocks"); } @Override @@ -82,6 +93,10 @@ public class ParkourConfiguration extends MiniGameConfiguration { for (Material material : killPlaneBlocks) { builder.append("\n - ").append(material.name()); } + builder.append("\n" + "Obstacle blocks: "); + for (Material material : obstacleBlocks) { + builder.append("\n - ").append(material.name()); + } return builder.toString(); } diff --git a/src/main/java/net/knarcraft/minigames/listener/MoveListener.java b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java index 029a16d..cc35fc4 100644 --- a/src/main/java/net/knarcraft/minigames/listener/MoveListener.java +++ b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java @@ -15,6 +15,7 @@ import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.config.SharedConfiguration; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -34,6 +35,8 @@ import java.util.Set; */ public class MoveListener implements Listener { + private static final BoundingBox fullBlockBox = new BoundingBox(0, 0, 0, 1, 1, 1); + private final DropperConfiguration dropperConfiguration; private final ParkourConfiguration parkourConfiguration; @@ -51,12 +54,15 @@ public class MoveListener implements Listener { @EventHandler public void onPlayerMove(PlayerMoveEvent event) { // Ignore if no actual movement is happening - if (event.getFrom().equals(event.getTo()) || event.getTo() == null) { + if (event.getTo() == null) { return; } ArenaSession session = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId()); if (session instanceof DropperArenaSession dropperSession) { + if (event.getFrom().equals(event.getTo())) { + return; + } doDropperArenaChecks(event, dropperSession); } else if (session instanceof ParkourArenaSession parkourSession) { doParkourArenaChecks(event, parkourSession); @@ -70,8 +76,7 @@ public class MoveListener implements Listener { * @param arenaSession

The dropper session of the player triggering the event

*/ private void doParkourArenaChecks(@NotNull PlayerMoveEvent event, ParkourArenaSession arenaSession) { - // Ignore movement which won't cause the player's block to change - if (event.getTo() == null || isSameLocation(event.getFrom(), event.getTo())) { + if (event.getTo() == null) { return; } @@ -228,7 +233,7 @@ public class MoveListener implements Listener { } } } else if (arena instanceof ParkourArena) { - return checkParkourDeathBlock(arenaSession, toLocation); + return checkParkourDeathBlock((ParkourArenaSession) arenaSession, toLocation); } return false; @@ -241,35 +246,49 @@ public class MoveListener implements Listener { * @param toLocation

The location the player is moving to

* @return

True if the player hit a death block

*/ - private boolean checkParkourDeathBlock(@NotNull ArenaSession arenaSession, + private boolean checkParkourDeathBlock(@NotNull ParkourArenaSession arenaSession, @NotNull Location toLocation) { - // If the player is standing on a non-full block, event.getTo will give the correct block, but if not, the - // block below has to be checked instead. - Set blocksBelow = getBlocksBeneathLocation(toLocation, 0); - Set adjustedBlocks = new HashSet<>(); - for (Block block : blocksBelow) { - if (block.getType().isAir()) { - block = block.getLocation().clone().subtract(0, 0.2, 0).getBlock(); - // Only trigger hit detection for passable blocks if the player is in the block - if (block.isPassable()) { - continue; - } - } - if (arenaSession.getArena().willCauseLoss(block)) { - adjustedBlocks.add(block); - } + // A simple check, only for kill blocks + if (isOnKillBlock(arenaSession, toLocation)) { + return true; + } + + // As the check for obstacle blocks is extensive, it's skipped if possible + Set obstacleBlocks = arenaSession.getArena().getObstacleBlocks(); + if (obstacleBlocks.isEmpty()) { + return false; } // Create a hit-box approximate to the player's real hit-box - double horizontalHitBox = ((ParkourArena) arenaSession.getArena()).getHorizontalKillPlaneHitBox(); - BoundingBox playerBox = new BoundingBox(-horizontalHitBox, -0.1, -horizontalHitBox, - 0.6 + horizontalHitBox, 1, 0.6 + horizontalHitBox).shift( + double playerHeight = 1.8; + Player player = Bukkit.getPlayer(arenaSession.getEntryState().getPlayerId()); + if (player != null && player.isSneaking()) { + playerHeight = 1.5; + } + BoundingBox playerBox = new BoundingBox(-0.05, -0.05, -0.05, + 0.6 + 0.05, playerHeight + 0.05, 0.6 + 0.05).shift( + toLocation).shift(-0.3, -0.05, -0.3); + BoundingBox playerPassableBox = new BoundingBox(0.2, 0.5, 0.2, + 0.4, playerHeight - 0.5, 0.4).shift( toLocation).shift(-0.3, 0, -0.3); - for (Block block : adjustedBlocks) { - // For liquids, or anything without a proper collision shape, trigger collision + Set possiblyHitBlocks = new HashSet<>(); + possiblyHitBlocks.addAll(getBlocksBeneathLocation(toLocation, 0, 0.01)); + possiblyHitBlocks.addAll(getBlocksBeneathLocation(toLocation, 1, 0.01)); + possiblyHitBlocks.addAll(getBlocksBeneathLocation(toLocation, -1, 0.01)); + possiblyHitBlocks.addAll(getBlocksBeneathLocation(toLocation, -2, 0.01)); + + for (Block block : possiblyHitBlocks) { + if (!obstacleBlocks.contains(block.getType())) { + continue; + } + + // For liquids, or anything without a proper collision shape, trigger collision if the player is partly + // inside when treated as a full block if (block.isLiquid() || block.getCollisionShape().getBoundingBoxes().isEmpty()) { - arenaSession.triggerLoss(); - return true; + if (playerPassableBox.overlaps(fullBlockBox.clone().shift(block.getLocation()))) { + arenaSession.triggerLoss(); + return true; + } } // Check whether the player's actual hit-box is intersecting with a block @@ -286,6 +305,34 @@ public class MoveListener implements Listener { return false; } + /** + * As simple check for whether a player is moving on top of a kill block + * + * @param arenaSession

The arena session the player is in

+ * @param toLocation

The location the player is moving to

+ * @return

True if the player is on a kill block, and a loss has been triggered

+ */ + private boolean isOnKillBlock(ParkourArenaSession arenaSession, Location toLocation) { + // If the player is standing on a non-full block, event.getTo will give the correct block, but if not, the + // block below has to be checked instead. + Set blocksBelow = getBlocksBeneathLocation(toLocation, 0); + Set killPlaneBlocks = arenaSession.getArena().getKillPlaneBlocks(); + for (Block block : blocksBelow) { + if (block.getType().isAir()) { + block = block.getLocation().clone().subtract(0, 0.2, 0).getBlock(); + // Only trigger hit detection for passable blocks if the player is in the block + if (block.isPassable()) { + continue; + } + } + if (killPlaneBlocks.contains(block.getType())) { + arenaSession.triggerLoss(); + return true; + } + } + return false; + } + /** * Gets the blocks at the given location that will be affected by the player's hit-box * @@ -293,12 +340,31 @@ public class MoveListener implements Listener { * @return

The blocks beneath the player

*/ private Set getBlocksBeneathLocation(Location location, double depth) { + return getBlocksBeneathLocation(location, depth, 0); + } + + /** + * Gets the blocks at the given location that will be affected by the player's hit-box + * + * @param location

The location to check

+ * @param extraRange

Extra range of the square used for finding blocks

+ * @return

The blocks beneath the player

+ */ + private Set getBlocksBeneathLocation(Location location, double depth, double extraRange) { Set blocksBeneath = new HashSet<>(); - double halfPlayerWidth = 0.3; + double halfPlayerWidth = 0.3 + extraRange; blocksBeneath.add(location.clone().subtract(halfPlayerWidth, depth, halfPlayerWidth).getBlock()); blocksBeneath.add(location.clone().subtract(-halfPlayerWidth, depth, halfPlayerWidth).getBlock()); blocksBeneath.add(location.clone().subtract(halfPlayerWidth, depth, -halfPlayerWidth).getBlock()); blocksBeneath.add(location.clone().subtract(-halfPlayerWidth, depth, -halfPlayerWidth).getBlock()); + // Once a certain size is reached, if the player is in the centre of a block, 9 must be accounted for + if (halfPlayerWidth > 0.5) { + blocksBeneath.add(location.getBlock()); + blocksBeneath.add(location.clone().subtract(halfPlayerWidth, depth, 0).getBlock()); + blocksBeneath.add(location.clone().subtract(-halfPlayerWidth, depth, 0).getBlock()); + blocksBeneath.add(location.clone().subtract(0, depth, -halfPlayerWidth).getBlock()); + blocksBeneath.add(location.clone().subtract(0, depth, halfPlayerWidth).getBlock()); + } return blocksBeneath; } diff --git a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java index f7c7135..ed3ef5d 100644 --- a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java @@ -135,7 +135,7 @@ public final class ParkourArenaStorageHelper { configSection.set(ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType())); configSection.set(ParkourArenaStorageKey.WIN_LOCATION.getKey(), arena.getWinLocation()); configSection.set(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey(), getKillPlaneBlocks(arena)); - configSection.set(ParkourArenaStorageKey.HORIZONTAL_KILL_PLANE_HIT_BOX.getKey(), arena.getHorizontalKillPlaneHitBox()); + configSection.set(ParkourArenaStorageKey.OBSTACLE_BLOCKS.getKey(), getObstacleBlocks(arena)); configSection.set(ParkourArenaStorageKey.CHECKPOINTS.getKey(), arena.getCheckpoints()); RewardStorageHelper.saveRewards(arena, configSection, ParkourArenaStorageKey.REWARDS.getKey()); saveParkourArenaData(arena.getData()); @@ -155,6 +155,20 @@ public final class ParkourArenaStorageHelper { } } + /** + * Gets a list of the obstacle blocks for the given arena + * + * @param arena

The arena to get obstacle blocks for

+ * @return

The obstacle blocks

+ */ + private static List getObstacleBlocks(ParkourArena arena) { + if (arena.getObstacleBlockNames() == null) { + return new ArrayList<>(); + } else { + return new ArrayList<>(arena.getObstacleBlockNames()); + } + } + /** * Loads all arenas * @@ -205,12 +219,17 @@ public final class ParkourArenaStorageHelper { List killPlaneBlockNamesList = configurationSection.getList(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey()); Set killPlaneBlockNames; if (killPlaneBlockNamesList == null) { - killPlaneBlockNames = new HashSet<>(); + killPlaneBlockNames = null; } else { killPlaneBlockNames = new HashSet<>((List) killPlaneBlockNamesList); } - double horizontalKillPlaneHitBox = configurationSection.getDouble( - ParkourArenaStorageKey.HORIZONTAL_KILL_PLANE_HIT_BOX.getKey(), 0); + List obstacleBlockNamesList = configurationSection.getList(ParkourArenaStorageKey.OBSTACLE_BLOCKS.getKey()); + Set obstacleBlockNames; + if (obstacleBlockNamesList == null) { + obstacleBlockNames = null; + } else { + obstacleBlockNames = new HashSet<>((List) obstacleBlockNamesList); + } List checkpoints = (List) configurationSection.get(ParkourArenaStorageKey.CHECKPOINTS.getKey()); Map> rewards = RewardStorageHelper.loadRewards(configurationSection, @@ -242,7 +261,7 @@ public final class ParkourArenaStorageHelper { } return new ParkourArena(arenaId, arenaName, spawnLocation, exitLocation, winBlockType.getRawValue(), winLocation, - killPlaneBlockNames, horizontalKillPlaneHitBox, checkpoints, rewards, arenaData, + killPlaneBlockNames, obstacleBlockNames, checkpoints, rewards, arenaData, MiniGames.getInstance().getParkourArenaHandler()); } diff --git a/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java b/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java index 3ceb900..213b515 100644 --- a/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java @@ -137,6 +137,7 @@ public final class TabCompleteHelper { List suggestions = new ArrayList<>(); suggestions.add("LAVA,MAGMA_BLOCK"); suggestions.add("WATER,MAGMA_BLOCK,LAVA,+BUTTONS,+CORALS"); + suggestions.add("CHAIN,END_ROD,LIGHTNING_ROD"); return suggestions; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8fee7e6..00181a9 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -20,6 +20,12 @@ parkour: killPlaneBlocks: - LAVA - MAGMA_BLOCK + + # The blocks treated as obstacles in a parkour arena, which will trigger a loss in any direction + obstacleBlocks: + - END_ROD + - LIGHTNING_ROD + - CHAIN dropper: # Whether to block using the shift key to drop faster than the intended drop speed blockSneaking: true