Solved Parkour Creation Concept

Discussion in 'Spigot Plugin Development' started by TacticalAxis, Jun 12, 2018.

  1. Hello Everyone.

    Okay, so I am creating a parkour for my server's hub (personal use), and I need a bit of help with the physics of it.

    This is my concept idea of it
    So I want to have the player right click a villager, (parkour mode on), which starts a timer (How do I create this timer?). Now as soon as they jump to the first block, if they try jumping back to the start, the parkour resets, and parkour mode is turned off for them. Now, another thing is, when they are on the other parkour blocks, I want to automatically detect if they fall off, and return them to the first parkour block. So how would I create the listeners to listen of any parkour failures. Remember this is going to be a really hard parkour and there are not going to be any checkpoints.

    This is not relating to the code, but rather the concept of the parkour. (What I don't understand, is what the listeners should be doing(for when a player falls off, when a player gets to the first block, etc.), how to create the timer (even inf multiple people are doing the parkour)).

    Sorry, this may be a little hard to understand, but for some reason I really couldn't find the right words to write on this forum post. LOL.

    Thanks in advance, and have a great day!

    P.S I kinda feel stupid for asking this type of question, as it is more concept oriented rather that code oriented, but I really can’t think of anything.
  2. Timer as in something that counts down or something that tracks how long they did the parkour in?
    If you want something that counts down you can use a BukkitRunnable object and set it for however long they have to complete the parkour. If you want to track how long it takes them, then you'll have to use either System.currentTimeMillis or System.nanoSeconds and subtract from the time that they started on. (See this for better explanation

    I don't think this would be the best approach, because if they jump back they might still want to do the parkour (maybe they're jumping back to go do it with a friend?). The better solution is to create a sign or a command that leaves them from parkour mode, but if you want to do this then its really just a matter of checking for location objects and seeing if the coordinates match up.

    Multiple ways of doing this. Simplest would be to make the parkours high enough from the ground so that the fall damage kills them and then you can check fro PlayerDeathEvent and teleport them back if they are in parkour mode && died from fall damage. Another way is just checking if their y coordinate is below a certain point, and then teleport them back to the first block. (Would need to make the first block's location persist)

    Understanding stuff conceptually is always harder than understanding code. Don't feel stupid because it is a legitimate question.
    • Useful Useful x 1
  3. I honestly think that I don't need the timer anymore, as there won't be any leader boards for this parkour (as there is a large reward at the end).

    I agree LOL. I think that I will go the command route, by giving the player an item for leaving as soon as they are in parkour mode. I think this is a lot more sensible than what I thought of initially.

    Ok. I will take the second option, but a bit different than as you specified. So I will *somehow* make a method trigger when a player lands on a block. For example: First I click on the start parkour NPC, then I start the parkour by jumping to the first block. MethodABC** is triggered when I land on that block. Then I keep jumping to the next, and the next block, and so on, until I reach the end of the parkour. Suppose I didn't land (missed the block) then as soon as I am further than 5 blocks away from the last block I was on (saved in playerLastLoc Hashmap) it resets me back to the start).

    *By the way, every single block location of the parkour is saved into a hashmap called blockLoc like so <String,Location>*
    *Also, when a player successfully lands on a block, their name and last sucessful block is stored in a hashmap called playerLastLoc like so <String,Location> (The string is the player name)*

    - Get location of the block the player is standing on, (variable playerLoc)
    - If playerLoc is not in the blockLoc hashmap, then reset player to the start (this means they jumped back onto the hub, as the parkour is literally like 5 blocks away from the side of the hub)

    Ok, that is how I am going to do it. Thanks alot for your suggestions, I really appreciate them @Perotin.
    Have a great day!
  4. Ok. So for anyone looking for the answer to this question, here it is:

    A value called startBlock is set as 0; // This is because we need to have a value of zero to start inserting the block locations later

    First I create three hashmaps. The first is called parkourMode, which stores the player object, and their name as a string(just a placeholder(not required)). The second hashmap is called blockLocation, which stores an integer, and the block location. The integer is there so that I can keep track of the amount of parkour jump locations (the block you jump to). The third hashmap called parkourDetails stores the parkour details, such as <"start",location>, and <"end",location>. The second and third hashmaps are temporarily there, as in reality, you'd want to store them in a config file or something.

    When you join the server, you go to the place you want to start off the parkour. You do /pk setstart, which adds <"start",p.getLocation> to the hashmap parkourDetails. *NOTE: p.getLocation is a location... Just saying - it is NOT TEXT*. Then you go and stand on the first parkour block, and type /pk +, which adds the block location to to hashmap by inserting <startBlock, p.getLocation()>. You continue to do this for all the parkour jumps. At the end, you type in /pk setend, which adds <"end",p.getLocation> to the hashmap parkourDetails.

    Now you want to play...

    You do /pk start, which gets the start location from the parkourDetails hashmap, and teleports you to it. You have now been added to the parkourMode hashmap. In a `PlayerMoveEvent` event, if you are in parkour mode, it checks if within 8 blocks of yourself, there is a location that you added earlier, then it allows you to continue, and if there isn't then it restarts you to the start of the parkour. If you do the parkour, and you are on the endpoint, or less that 0.95 blocks distance from the endpoint, it teleports you back to the start, telling you that you won it, and then removes you from the parkourMode hashmap.

    As of typing this, I am really tired, so for those who really don't understand what I am saying, or others who just wanna be spoonfed some code, here it is. For others, if you can suggest any improvements (hopefully in efficiency), that would be great!

    Core Class
    Code (Java):
    import org.bukkit.Location;
    import org.bukkit.entity.Player;

    import java.util.HashMap;
    import java.util.Map;

    public class Core extends JavaPlugin {

        private static Core instance;
        public Map<Player, String> parkourMode = new HashMap<>();
        public Map<Integer, Location> blockLoc = new HashMap<>();
        public Map<String, Location> parkourDetails = new HashMap<>();
        public int startBlock = 0;

        public static Core getInstance() {
            return Core.instance;

        public void onEnable() {
            Core.instance = this;
            getServer().getPluginManager().registerEvents(new Parkour(), this);
            getCommand("pk").setExecutor(new Commands());
            Core.getInstance().parkourMode = parkourMode;
            Core.getInstance().blockLoc = blockLoc;
            Core.getInstance().parkourDetails = parkourDetails;
            Core.getInstance().startBlock = startBlock;


        public void onDisable() {
            Core.instance = null;
    Parkour Class
    Code (Java):
    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.player.PlayerMoveEvent;

    import java.util.Map;

    public class Parkour implements Listener {

        public static boolean isDoingParkour(Player player) {
            return Core.getInstance().parkourMode.containsKey(player);

        public static void enableParkourMode(Player player) {
            Core.getInstance().parkourMode.put(player, player.getName());

        public static void disableParkourMode(Player player) {

        public void onJump(PlayerMoveEvent event) {
            Player player = event.getPlayer();
            boolean setting = false;
            if (isDoingParkour(player)) {
                for (Map.Entry<Integer, Location> entry : Core.getInstance().blockLoc.entrySet()) {
                    if(player.getLocation().distance(Core.getInstance().parkourDetails.get("end")) <= .95) {
                        player.sendMessage(ChatColor.GREEN + "Yay! You won the parkour!");
                    } else if(entry.getValue().distance(player.getLocation()) <= 8 ) {
                        setting = true;
                if(!setting) {
    Commands Class
    Code (Java):
    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;

    import java.util.Arrays;
    import java.util.Map;

    public class Commands implements CommandExecutor {

        public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
            if (cmd.getName().equalsIgnoreCase("pk")) {
                if (sender instanceof Player) {
                    Player player = (Player) sender;
                    if (args.length == 1) {
                        if (args[0].equalsIgnoreCase("setstart")) {
                            Core.getInstance().parkourDetails.put("start", player.getLocation());
                            player.sendMessage(ChatColor.GREEN + "Start was set!");
                        } else if (args[0].equalsIgnoreCase("setend")) {
                            Core.getInstance().parkourDetails.put("end", player.getLocation());
                            player.sendMessage(ChatColor.GREEN + "End was set!");
                        } else if (args[0].equalsIgnoreCase("+")) {
                            try {
                                Location location = new Location(player.getWorld(), player.getLocation().getX() + 0, player.getLocation().getY(), player.getLocation().getZ() + 0, 0, 0);
                                Core.getInstance().blockLoc.put(Core.getInstance().startBlock, location);
                            } catch (Exception e) {
                            player.sendMessage(ChatColor.GREEN + "Block added!");
                        } else if (args[0].equalsIgnoreCase("-")) {
                            player.sendMessage(ChatColor.GREEN + "Block Points Reset!");
                        } else if (args[0].equalsIgnoreCase("start")) {
                            if (Core.getInstance().parkourDetails.get("start") != null && Core.getInstance().parkourDetails.get("end") != null) {
                                player.sendMessage(ChatColor.GREEN + "Started!");
                                for (Map.Entry<Integer, Location> entry : Core.getInstance().blockLoc.entrySet()) {
                                    String key = Integer.toString(entry.getKey());
                                    String value = String.valueOf(entry.getValue());
                                    System.out.println(key + " : " + value);
                            } else {
                                player.sendMessage(ChatColor.RED + "Error: No Setup");
                                for (Map.Entry<Integer, Location> entry : Core.getInstance().blockLoc.entrySet()) {
                                    String key = Integer.toString(entry.getKey());
                                    String value = String.valueOf(entry.getValue());
                                    System.out.println(key + " : " + value);
                        } else if (args[0].equalsIgnoreCase("end")) {
                            if (Core.getInstance().parkourMode.get(player) != null) {
                                player.sendMessage(ChatColor.GREEN + "Ended!");
                            } else {
                                player.sendMessage(ChatColor.RED + "Error: You didn't start!");

                } else {
                    sender.sendMessage(ChatColor.RED + "This is a player command!");

            return false;
    Please ignore the fact that didn't include package names.

    It was really late at night for me while typing this, so if ANYONE read it at all, I really appreciate it.

    Have a great day/night wherever you are :)

    Special Thanks to @Perotin for helping me out without this concept. I know you guys might be cringing out right now, but I really couldn't think of anything, and without @Perotin I would have probably taken a lot longer to figure this out.

    P.S. If anyone figured that I was also previously talking about giving players an item(to leave), I haven't implemented that yet, but it's not hard - there are sooooo many tutorials out there on the web. Here let me Google that for you
    #4 TacticalAxis, Jun 13, 2018
    Last edited: Jun 13, 2018