[SOLVED-ish] Get player's position in relation to minecart

Discussion in 'Plugin Development' started by Taco, Apr 28, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Taco

    I'm at a loss here. I'm looking for a way to tell is a player is in front of or behind a minecart. Does anyone know how this could be achieved?
     
  2. Offline

    Musaddict

    This is how I'd do it:
    • Save the last direction the minecart is moving in a hashmap.
    • On a player movement event see if they are in the same plane as the cart.
      • if the direction is East / West, then compare the X values to see if they match
      • if the direction is North / South, then compare the Z values to see if they match
    • If they match, then calculate the the difference in X or Z differences
      • if the direction is East / West, then player.getLocation().getZ() - cart.getLocation.getZ()
      • if the direction is North / South, then player.getLocation().getX() - cart.getLocation.getX()
    • Depending on if the difference is positive, you will be able to tell which direction the player is from the cart.
      • East / West and positive difference, East
      • East / West and negative difference, West
      • North / South and positive difference, North
      • North / South and negative difference, South
    • If the player passed the same-plane check (in the second bullet), then compare the direction of the cart to the calculated relative direction to the player. If they match, in front. Else, behind.
    I would give you some sample code, but I have not worked with minecarts, but know that all of these methods should still apply. Probably an easier way, but this is my workaround.
     
  3. Offline

    Njol

    I'd use vectors:
    Code:java
    1. static boolean isInFrontOf(Player p, Minecart cart) {
    2. if (cart.getLocation().distance(p.getLocation()) > 2)
    3. return false;
    4. return cart.getVelocity().normalize().dot(p.getLocation().toVector().subtract(cart.getLocation().toVector()).normalize()) >= Math.cos(Math.PI/4);
    5. }

    This basically checks whether the player is within a 2 meter radius of the minecart, and if he is, it checks whether he is in front of the minecart. This is done by checking whether the angle between the minecart's velocity vector and the vector from the minecart to the player is smaller than 45° (pi/4).

    edit: changed <= to >=
     
  4. Offline

    Taco

    Thank you very much sir. I'm not familiar with normalize though, and what sort of operation it performs on the vectors, so I will look that up for future use. I very much appreciate your response.
     
  5. Offline

    Njol

    I use this equation:
    Code:
    dot(a,b) = length(a)*length(b)*cos(angle)
    and if I normalize the vectors, their length becomes 1, thus dot(a, b) = cos(angle).
    This made me realize a mistake in the code above: It sould be >=, not <= in the last line (as the cosinuses are compared, not the angles themselves, and the cosinus of an angle gets smaller as the angle gets bigger for angles < 180°)
     
  6. Offline

    Taco

    Ahh alright. When I learned vectors in precalc, they taught normalizing by calling the process "converting to a unit vector". I thank you again for this.

    Njol I've been toying about with your code that you provided, and it only seems to return true about 60% of the time with just this:
    Code:
    static boolean isInFrontOf(Player p, Minecart cart) {
    return cart.getVelocity().normalize().dot(p.getLocation().toVector().subtract(cart.getLocation().toVector()).normalize()) <= Math.cos(Math.PI/4);
    }
    I can't figure out why it's doing that myself. Have any ideas?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 25, 2016
  7. Offline

    Njol

    Change <= to >=, this was a mistake in the first code.
     
  8. Offline

    Taco

    I did that. I was being lazy and copy/pasted from your post for that. My code has that modification and is slightly different.
     
  9. Offline

    Njol

    Are you testing this with a moving minecart? For a static minecart the function will always return true since it's velocity is 0. If you want it to work with non-moving minecarts as well you have to use the cart's direction and it's inverse, not it's velocity. This way you'll also find out on which side of the minecart the player is, though since I don't know what you're needing this for, I can't really help you further.
     
  10. Offline

    Taco

    I'm using it for moving minecarts. This check is being run when the minecart collides with a living entity.
     
  11. Offline

    Njol

    Try replacing cart.getVelocity() with cart.getLocation().getDirection(), as the minecart might already have stopped before the event is called. Maybe you also have to increase the angle a bit, currently it's 45°.
    btw: Why don't you use VehicleEntityCollisionEvent.getEntity() to get the collided entity?
     
  12. Offline

    Taco

    I do use that. I'll try using the direction aswell. I'll post all my code later today so that you can get an idea of what I'm doing.

    Njol Here's my current code:

    Code:
        @EventHandler
        public void onVehilceEntityCollide(VehicleEntityCollisionEvent event)
        {
            Entity e = event.getEntity();
            if(e instanceof LivingEntity)
            {
                Vehicle v = event.getVehicle();
                LivingEntity le = (LivingEntity) e;
                boolean front = isInFrontOf(le,v);
                System.out.println(front);
                if(front)
                {
                    int dmg = (int) (event.getVehicle().getVelocity().length() * 10);
                    EntityDamageEvent damage = new EntityDamageEvent(le,DamageCause.ENTITY_ATTACK, dmg);
                    Bukkit.getPluginManager().callEvent(damage);
                    if(!damage.isCancelled())
                    {
                        if(e instanceof Player)
                        {
                            Player p = (Player) e;
                            p.sendMessage(Integer.toString(dmg));
                            if(dmg >= p.getHealth())
                                plugin.hitPlayers.add(p.getName());
                        }
                        if(dmg>0)
                        {
                            le.damage(dmg);
                        }
                    }
                }
                //v.setVelocity(v.getVelocity().multiply(-1));
            }
        }
        boolean isInFrontOf(LivingEntity l, Vehicle v) {
            return v.getVelocity().dot(l.getLocation().toVector().subtract(v.getLocation().toVector()).normalize()) >= Math.cos(Math.PI/4);
        }
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 25, 2016
  13. Offline

    Njol

    Nice idea you have there making minecarts a dangerous vehicle. I guess the problem's source is the minecart's center being on a different level than a player's center (center here means where entity.getLocation() points to). You can either change the isInFrontOf check to check a greater angle and/or find the y-coordinate of the centers, e.g. by printing both locations on collision, and adjusting the minecart's center in the check by the difference (though that might not work well for (cave) spiders for example, since their center likely is lower than that of other creatures)
     
  14. Offline

    Taco

    I can easily create a switch statement to determine the offset needed. For now I'm trying it out on players. I'll see what I can work out about trying to get the elevation the same and see if that helps any.

    I've got an idea for an entirely different way of handling this. I'll post back when I get home and get tot testing it.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 25, 2016
  15. Offline

    Taco

Thread Status:
Not open for further replies.

Share This Page