Spawn block under player when landing?

Discussion in 'Spigot Plugin Development' started by Remy2402, Sep 17, 2019.

  1. Hi there, did a search for something like this and couldn't find anything.

    I'm creating some "slime boots" and want a slime block to spawn under a player when they are about to hit the ground. This also needs to work so that if they jumped from a high place, the slime block would launch them as if they had been falling from that height like normal if that makes sense?

    Thanks in advance
     
  2. can you show what you have already tried?
     
  3. drives_a_ford

    Moderator

    I don't think there's a simple way to know when a player is "about to land". Your best bet would be to get the player's bounding box and check if there's a block under them close by. You'd need to do so in the PlayerMoveEvent.

    You'd probably need to keep track of the players that are actually wearing said "slime boots" so you don't do a lot of calculations every tick for every online player. And you'd most likely want to check Entity#getFallDistance to make sure the player is falling before doing any extensive calculations.

    Sounds like a cool idea, though.

    EDIT:
    I guess I forgot to comment on the "bounding box". The reason you need to check all 4 corners is because the player can land on a block even if it's center (i.e its getLocation) is not above said block.

    EDIT2:
    Using the bounding box check you'd also need to take care to check for bounding boxes of the blocks you're about to (potentially) land on (i.e a fence or an end rod have significantly smaller hitboxes).
    All in all, not something that can be calculated fast.
     
  4. Yeah I feared it wasn't going to be simple. Would checking for the block under them using your hypothetical method be too taxing to run on every PlayerMoveEvent?
     
  5. You can do this on player move event but will cost calculations time
    calculate if from location Y is higher than to location y
    if it is then the player is going down
    now check the to location Y - 0.1 if it is a slime block if it is then
    calculate forward Vector and apply the needed velocity
    [edit]
    World#raytraceBlocks(); to calculate the exact bounding box
    This will work on stairs slabs
     
  6. drives_a_ford

    Moderator

    I don't think you'll have a choice.

    But "too taxing"? It's hard to tell and depends on a lot of other variables.
    I.e do you expect a lot of people to be wearing such boots?

    Your best bet would be to make sure you're only running this bit of code for the people who are a) wearing those boots and b) actually falling. I don't think there's much more optimization you can do here. There is simply no way to predict the future.

    You might get away with calculating all of this every other tick (or something like this), but then you'd need to check for a block further down instead. It's a matter of testing, I've not done anything like this myself.

    You should be able to keep track of whether or not someone's wearing the special boots relatively simply. So if you keep these player's UUIDs in a HashSet, the check itself wouldn't be too demanding and thus you'd generally not run a lot of code at for each player's move event unless they're wearing the boots (and are falling).

    EDIT:
    Right now it's actually not possible to get the exact bounding box of a block through the API as Block#getBoundingBox mentions.
    You would be able to use World#rayTraceBlock, however that's far from a cheap computation.
     
    #6 drives_a_ford, Sep 17, 2019
    Last edited: Sep 17, 2019
  7. Can you not just apply velocity to the player then they land if they have the slime boots on.

    Try looking at the code that the slime block uses
     
  8. Just check when the boots get applied. I can show you a way to do this if you don't know how. Once applied add them into an ArrayList
    Code (Text):

    HashSet<UUID> players = new HashSet<UUID>();
    players.add(p.getUniqueId());
     
    start a Bukkit Runnable after

    Code (Text):

    Location loc = p.getLocation();
    World world = p.getWorld();
    double x, y, z;
    Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(Main.plugin, (Runnable) BukkitRunnable(){
        public void run(){
            if(players.contains(p.getUniqueId()){
                if(!p.isFlying() && !p.isOnGround() && p.getVelocity().getY() < 0){
                    //check if persons y block - 1 is not null, if it is not, place a block beneath them.
                    x = loc.getX();
                    y = loc.getY() - 1;
                    z = loc.getZ();
                    loc = new Location(world, x, y, z);
     
                    if(loc.getBlock() != null){
                        loc.getBlock().setType(Material.GRASS);
                   }
                }
             }
            else {
                cancel();
            }
        }
    }.runTaskTimer(Main.plugin, 5L, 5L));
     
     
    #8 Wheredabanat, Sep 17, 2019
    Last edited: Sep 17, 2019
  9. drives_a_ford

    Moderator

    Please don't use an ArrayList for this. I already mentioned this, but a HashSet is far better in this situation because of how much more efficient its #contains method is.

    EDIT:
    Not to mention the fact that you in no way need to check this every tick. you only ever need to check when/if a player is moving, or more specifically, falling.
    EDIT2:
    And I guess you totally missed what I said earlier:
    Your method can and will make the player splash to death when you land on a corner of a block because the block underneath the center of the player will be air yet the player lands on another block.
     
  10. Should be good now @drives_a_ford
    Thanks for telling me about the HashSet, I never knew the .contain method is more efficient than ArrayList's .contain.