Getting the person who got shot

Discussion in 'Plugin Development' started by MistaKewl, Mar 25, 2015.

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

    MistaKewl

    Code:
        @EventHandler
        public void onShoot(EntityShootBowEvent e) {
            LivingEntity p = e.getEntity();
            ItemStack bow = e.getBow();
            ItemMeta b = bow.getItemMeta();
            if(b.getLore().contains("§7Poison I")) {
                p.addPotionEffect(new PotionEffect(PotionEffectType.CONFUSION, 1000, 1000));
                p.setMaxHealth(40);
                p.addPotionEffect(new PotionEffect(PotionEffectType.HEAL, 100, 100));
            }
        }
    Hey there Bukkit community! I am working on a Custom Enchantments plugin [But only uses lores and Entity Damage Events] I have everything setup for a sword which has a, for example ("Poison I") lore. It will get the person who was hit and poison them. I want to do the same with a bow. I've tried ProjectileHitEvent BowShootEvents to get the person who got hit and give them a potion effect I can only do this to the Person who SHOT the bow and not the "Shotee" I want to give the person who got shot a Potion effect if the lore of the Bow is "Poison I". But don't know how. It'd be great if someone helped me out! Thank you.

    Below is my code for giving the person who shot the bow the effect which I don't want :p (Attempt)
     
  2. Offline

    mine-care

    Read documentation. it is your friend!
    you need entityDamageByEntityEvent and check if the damager is an arrow. I strongly recomend you add metadata to the shot arrows in order to trace them back in the damage event.
     
  3. Offline

    MistaKewl

    @mine-care Thanks for your help! I was thinking that :p I'll try it in a sec

    @mine-care Also, could I have a little example

    <Edited by bwfcwalshy: Merged posts, please use the edit button rather than double posting.>
     
  4. Offline

    Totom3

    @MistaKewl

    Code:
    void on(EntityDamageByEntityEvent evnt) {
        Entity damaged = evnt.getEntity();
        Entity damager = evnt.getDamager();
    
        if (damager instanceof Arrow && damaged instanceof Player) {
            // Do stuff
        }
    }
     
  5. Offline

    MistaKewl

    @Totom3
    Thanks for your help! But how would I get the bow that it was shot from? So I can get it's ItemMeta and check for any lores? Thanks.
     
  6. Offline

    Konato_K

    @MistaKewl You can't, that's why it was suggested to use Metadata, alternatively you can store the special arrows in a collection.
     
  7. Offline

    MistaKewl

    @Konato_K
    Sorry, I'm not the best a coding (Begginer-ish) could you please explain that a little more? Thank you. Appreciate your help.
     
  8. Just google it. Is that so hard /.-
    Metadata can be put on any entity to store/read information
     
  9. Offline

    Konato_K

    @MistaKewl Entities are Metadatable, so you can attach metadata values to them.

    I won't explain collections since that is basic java knowledge
     
  10. Offline

    Tecno_Wizard

    @MistaKewl, if you're new, metadata isn't something you want to be working with. I suggest using a hashmap saving the arrow's UUID as the key and the lore as the value. Use the projectileFireEvent (I have no idea what the name of this class is, but I've used it and know it exists) and if the shooter is a player and the projectile is an arrow, save that arrow into a hashmap using the arrow's UUID as the key and the special effect as the value (just don't set the arrow in the hashmap if there is no enchant). On the damage event, check to see if the damager is an arrow, then check to see if the arrow's UUID is in the hashmap. If it is, get the lore and apply. Make sure to delete the arrow's entry in the hashmap though.
     
  11. Offline

    Totom3

    @MistaKewl The best approach would probably be to set the arrow's metadata. So for example you define a set of variables that will be applied when the arrow is fired. For example:

    Storing the relevant data directly (prefered):
    Key="minDamage": a double representing minimum damage
    Key="maxDamage": another double representing maximum damage

    Storing the bow:
    Key="bow": the ItemStack of the bow used to fire the arrow

    I suggest the first approach as it doesn't require parsing (if you decide to store the ItemStack, you'll need to parse the lore). In any case I suggest you don't go for Tecno_Wizard's approach, especially if you're a beginner. End up with memory leaks.
     
  12. Offline

    Tecno_Wizard

    @Totom3, I can post my metadata utilities class later today for him. Should make it incredibly simple to use metadata.
     
  13. There is no need for a extra class.
    Just three methods:
    Code:java
    1. public void addMetadata(Entity e, String key, Object value) {
    2. e.setMetaData(key, new FixedMetadataValue(plugin, value));
    3. }
    4.  
    5. public void removeMetadata(Entity e, String key) {
    6. e.removeMetadata(key, plugin);
    7. }
    8.  
    9. public String getMetadata(Entity e, String key) {
    10. return e.hasMetadata(key) ? e.getMetadata(key).get(0).asString() : null;
    11. }

    and to check if an entity has a Metadata:
    Code:java
    1. if (e.hasMetadata("key")) {

    plugin is the main class (extends JavaPlugin)

    More infos about Metadata:
    http://www.javaminecraft.com/bukkitapi/org/bukkit/metadata/Metadatable.html
    and
    http://www.javaminecraft.com/bukkitapi/org/bukkit/metadata/MetadataValue.html
     
    MaTaMoR_ likes this.
  14. Offline

    Tecno_Wizard

    @FisheyLP, You're making that look far more simple than it is. There are more return values than that and your parameters are not fully generic. Plus, I can break it with just a few lines.

    EDIT: if anyone finds something that is broken in here, please tell me. I edited this on notepad without a syntax checker...
    Code:
    package com.tecno_wizard.metaUtils
    
    import com.sun.browser.plugin.Plugin;
    import org.bukkit.metadata.MetadataValue;
    import org.bukkit.metadata.Metadatable;
    import org.bukkit.plugin.java.JavaPlugin;
    
    import java.util.List;
    
    /**
      * Created by Tecno_Wizard w/o any licensing. Use as you'd like.
      */
    public class MetaUtils {
        //commented out because in development
        //public enum classType{BOOLEAN, BYTE, SHORT, INTEGER, FLOAT, DOUBLE, LONG, STRING};
    
        private String pluginName = "";
    
        /**
         * Creates a new MetaUtils instance
         * @param plugin Plugin
         */
        public MetaUtils(JavaPlugin plugin) {
            this(plugin.getName());
        }
    
        /**
         * Creates a new MetaUtils instance.
         * @param pluginName Name of the plugin
         */
        public MetaUtils(String pluginName) {
            this.pluginName = pluginName;
        }
    
        /**
         * Sets the plugin name used when inspecting metadata.
         * @param name Name of the plugin (must be exact)
         */
        public void setPluginName(String name){
            pluginName = name;
        }
    
        /**
         * Returns whether or not metadata exists under the given key
         *
         * @param metadatable
         *            Metadatable to check for metadata
         * @param key
         *            Key to check for metadata
         * @return Whether or not the specifed Metadata exists
         */
        public boolean hasMetadata(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return true;
            }
            return false;
        }
    
        /**
         * Gets the specified metadata as a boolean.
         * If no data is found under the key, return value will be false.
         *
         * @param metadatable
         *            Metadatable to get data from
         * @param key
         *            Key to get data from
         * @return Value of metadata
         */
        public boolean getMetadataValueAsBoolean(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asBoolean();
            }
            return false;
        }
    
        /**
         * Gets the specified metadata as a String.
         * If no data is found under the key, return value will be "".
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public String getMetadataValueAsString(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asString();
            }
            return "";
        }
    
        /**
         * Gets the specified metadata as a byte.
         * If no data is found under the key, return value will be 0.
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public byte getMetadataValueAsByte(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asByte();
            }
            return 0;
        }
    
        /**
         * Gets the specified metadata as a double.
         * If no data is found under the key, return value will be 0.0
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public double getMetadataValueAsByte(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asDouble();
            }
            return 0D;
        }
    
        /**
         * Gets the specified metadata as a float.
         * If no data is found under the key, return value will be 0.0
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public float getMetadataValueAsFloat(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asFloat();
            }
            return 0;
        }
    
        /**
         * Gets the specified metadata as a short.
         * If no data is found under the key, return value will be 0
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public short getMetadataValueAsShort(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asShort();
            }
            return 0;
        }
    
        /**
         * Gets the specified metadata as an int.
         * If no data is found under the key, return value will be 0.
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public byte getMetadataValueAsInt(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asInt();
            }
            return 0;
        }
    
        /**
         * Gets the specified metadata as a long.
         * If no data is found under the key, return value will be 0.
         *
         * @param metadatable Metadatable to get data from
         * @param key Key to get data from
         * @return Value of metadata
         */
        public byte getMetadataValueAsLong(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val : values) {
                if (val.getOwningPlugin().getName().equals(pluginName))
                    return val.asLong();
            }
            return 0;
        }
    
        //commented out because it is in development, trying to figure out how to work it
        /*
        public classType getValueUnderKey(Metadatable metadatable, String key) {
            List<MetadataValue> values = metadatable.getMetadata(key);
            for (MetadataValue val: values) {
                if(val.getOwningPlugin().getName().equals(pluginName))
                    if()
            }
        }
        */
     
    }
    
    EDIT 2: I just realized that I could have done this WAY more efficiently, made this months ago. XD
     
    Last edited: Mar 27, 2015
  15. Offline

    Totom3

    @FisheyLP @Tecno_Wizard What I'm mostly concerned about is that you both forgot that it is possible to set any Object as metadata, and that it's not restrained to primitive types (and Strings).
     
  16. Offline

    Konato_K

    @Tecno_Wizard @FisheyLP I'm more concerned that you need a class to handle metadata when it isn't that hard .-.

    Anyway, let's wait for OP to answer?
     
  17. Offline

    Tecno_Wizard

    @Konato_K, lol. I know it isn't that hard, but it makes my code MUCH cleaner. I wrote the entire class, I know how to use metadata. lol.

    @Totom3, never had a reason to use an object, but I have used the others.
     
    Last edited: Mar 27, 2015
  18. Offline

    MistaKewl

    @Konato_K @Totom3 @Tecno_Wizard @FisheyLP
    Thank you all of you! Appreciate the help :D I'll do my best to use all this in the right way. I'll tell you guys once I'm done, I guess!

    EDIT: Nevermind, I'm just confused now... :/


    Just to clarify :p
    What I'm trying to do:
    1) Whenever someone shoots an arrow from a bow with the lore "Poison I" it will give the player who was shot a Poison potion effect. If that a bit too hard for me, possibly with arrows and checking if they have the lore "Poison I" and then the person who was shot from that arrow with receive poison. Anything among those lines. Sorry if it's asking for to much! I've tried many things I just can't get it right. Once again, thanks.


    I feel so noob-ish :l
     
    Last edited: Mar 28, 2015
  19. There is a simpler way:
    1. ProjectileLaunchEvent: Check if players item in hand contains the lore.
    2. Add the projectiles UUID to a List<UUID>
    3. EntityDamageByEntity: Check if the list contains the damagers(arrow) UUID
    4. Remove the damagers(arrow) UUID from list
    5. Give the hit entity potioneffect
     
  20. Offline

    Tecno_Wizard

    @FisheyLP, I realized that that is a memory leak. Arrow doesn't hit target.
     
  21. Offline

    Totom3

    @MistaKewl Don't feel sorry, it's mostly our fault... First of all keep in mind that there will always be thousands of ways of achieving your goals in programming. Techo_Wizard, Fishey and I have suggested 3 here. It's up to you to choose one or make your own.

    The problem: when the arrow hits an Entity, you want to give the said Entity a poison effect. However, not ALL arrows can do this, only those shot from a special, predefined bow.

    Breakdown:
    1) Since not all arrows can give poison effects, you need to listen to EntityShootBowEvent and flag the arrow, in some way, as special, if it was shot from the poison bow.
    2) When the arrow hits, you'll want to check whether or not the said arrow is special, in which case you give the shot Entity a poison effect.

    The part we were mostly discussing is the way you should flag the arrow as special.

    1. Using Metadatas (by me):
    Actually, nevermind. I thought Entity metadatas were removed when said Entity dies, which is not the case. Sorry.

    2. Using a List (by @FisheyLP):
    Arrows considered special have their UUID stored in a List. To check if an arrow is special, you check whether or not it's UUID is present in the List. I don't like this approach first of all because a Set would be much better than a List in this situation (at your stage it's normal if you don't make the difference). Both are used almost the same way. Secondly I suppose you'll want eventually to make more special arrows, such as a fire arrow, ice arrow, stun arrow, etc... If you decide to take this approach, you'll need to make a separate list/set per arrow type, which is not necessarily the best thing to do. However I must acknowledge the simplicity of this solution.

    Example:
    Example with Set (open)
    Code:
    // Using a Set over a List for efficiency
    Set<UUID> set = new HashSet<>();
    
    // EntityShootBowEvent (when the arrow is fired):
    set.add(projectile.getUUID());
    
    // EntityDamageByEntityEvent (when the arrow hits a target):
    if (set.remove(damager.getUUID()) {
       // The arrow is special, now apply poison
    } else {
       // The arrow is not special
    }



    2. Using a Map (by @Techno_Wizard):
    The concept here is very similar to what Fishey suggested. Instead of using a List, we are using a Map (google that if you don't know what it is). This in turn allows you to set data for each special arrow, such as the arrow type, the level of the effects, the duration, etc... The possibilities are endless. I find this approach better because if one day you want more arrow types or custom effect durations, you'll be able to implement that in a matter of seconds.

    Example:
    Example with Map (open)
    Code:
    // For each arrow, the lore of the bow that shot it is stored
    Map<UUID, List<String>> map = new HashMap<>();
    
    // EntityShootBowEvent (when the arrow is fired):
    map.put(projectile.getUUID(), bowItemStack.getItemMeta().getLore());
    
    // EntityDamageByEntityEvent (basically when the arrow hits a target):
    List<String> lore = map.remove(projectile.getUUID());
    
    // Parse the lore, check for "POISON I" or whatever you decided to set as lore


    In any case, remember to get rid of the data when the arrow hits (that's why I used remove() instead of get() and contains() ), or you'll end up with memory leaks.
     
    MistaKewl, FisheyLP and seanliam2000 like this.
  22. Offline

    MistaKewl

    @Totom3
    Thank you :) But in FisheyLP's method how would I check if the bow has the Poison Lore?


    Nevermind - Sorry!
     
  23. Offline

    nverdier

  24. Offline

    MistaKewl

    Didn't work, I made a noobish mistake somewhere but have no idea where..
    Code:
    @EventHandler
        public void onEntityDamage(EntityDamageByEntityEvent e) {
            Player damager = (Player) e.getDamager();
            Player hit = (Player) e.getEntity();
            Entity projectile = e.getEntity();
            if (damager instanceof Arrow && hit instanceof Player) {
                Arrow arrow = (Arrow) e.getEntity();
                List<String> lore = new ArrayList<String>();
                lore.add("§7Blindness I");
                if (set.remove(damager.getUniqueId())) {
                    hit.addPotionEffect(new PotionEffect(PotionEffectType.POISON, 100, 100));
                } else {
                    // The arrow is not special
                }
                    if (e.getDamager() instanceof Player) {
                            if (damager.getItemInHand().getItemMeta().getLore().equals("§7Poison I")) ;
                            {
                                hit.addPotionEffect(new PotionEffect(PotionEffectType.POISON, 10, 10));
                            }
                            if (damager.getItemInHand().getItemMeta().getLore().equals("§7Blindess I")) ;
                            {
                                hit.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 100));
                            }
                        }
    
                    }
                }
    Code:
    @EventHandler
        public void onShoot(EntityShootBowEvent e) {
            LivingEntity p = e.getEntity();
            ItemStack bow = e.getBow();
            ItemMeta b = bow.getItemMeta();
            Entity projectile = e.getProjectile();
            if(b.getLore().contains("§7Poison I")) {
                set.add(projectile.getUniqueId());
            }
        }
    I know about the .getUUID() being .getUniqueId(); Or is that what I did wrong?


    *** Something to do with Player damager = (Player) e.getDamager();
     
    Last edited: Mar 28, 2015
  25. You casted the damager and hit to Player and before that checked if its instanceof Arrow. It will never be true.
    Those 2 entities can be any entity like Creeper, Ghast, Spider, Snowball and not only Player.

    Do it like this:
    Code:
    Entity damager = e.getDamager();
    Entity hit = e.getEntity();
    if (damager instanceof Arrow && hit instanceof Player) {
    //HERE YOU CAN CAST THEM TO ARROW AND PLAYER
     
    Last edited: Mar 28, 2015
  26. Offline

    MistaKewl

    @FisheyLP But then I can't check the ItemInHand for the Person who is hitting and give potion effects to the hit player
     
  27. I give you an example:
    List:
    A random player shoots with a bow.
    The bow has the lore §7Blindness I
    Now the projectiles UUID is added to a list because the bow has the lore.
    We don't need the ItemStack anymore, because only those "special" projectiles (with §7Blindness I) are beeing added to the list.
    On the EntityDamageByEntityEvent, we check if the damager is an Arrow and if the list contains the arrows UUID.
    If it does, the arrows UUID gets removed from the list and your code happens.

    Or you can instead make a HashMap<UUID, List<String>> to save the arrows UUID and the lore (to have more possibilities of effects in the lore):
    HashMap:
    A random player are shooting with a bow.
    The bow has the lore §7Posion II.
    The projectiles UUID(key) is put to the hashmap with the lore(value)
    We don't need the ItemStack anymore, because the lore (effect) is already in the HashMap.
    On the EntityDamageByEntityEvent, we check if the damager is an Arrow and if the list contains the arrows UUID.
    If it does, we save the List<String> we are getting from the HashMap with hashmap.get(key);
    Then the arrows UUID gets removed from the list and your code happens:
    Example: if (lore.contains("Posion I")) {
     
    Last edited: Mar 28, 2015
    MistaKewl likes this.
  28. Offline

    MistaKewl

    @FisheyLP
    Code:
    @EventHandler
        public void onEntityDamage(EntityDamageByEntityEvent e) {
            Entity damager = e.getDamager();
            Entity hit = e.getEntity();
            if(damager instanceof Arrow && hit instanceof Player) {
                Player damager2 = (Player) e.getDamager();
                Player hit2 = (Player) e.getEntity();
                if (set.remove(damager.getUniqueId())) {
                    hit2.addPotionEffect(new PotionEffect(PotionEffectType.POISON, 100, 100));
                } else {
                    // The arrow is not special
                }
                if (e.getDamager() instanceof Player) {
                    if (damager2.getItemInHand().getItemMeta().getLore().equals("§7Poison I")) ;
                    {
                        hit2.addPotionEffect(new PotionEffect(PotionEffectType.POISON, 10, 10));
                    }
                    if (damager2.getItemInHand().getItemMeta().getLore().equals("§7Blindess I")) ;
                    {
                        hit2.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 100));
                    }
                }
            }
        }
    Nothing happens and I get an error in the console about the Player damager2 = (Player) e.getDamager();
     
  29. You checked if damager is an instance of Arrow and then cast it to a Player... Thats your error.
    And another thing: You don't need to make new variables like damager2 and hit2.
    Just cast them:
    Code:
    damager = (Arrow) damager;
    hit = (Player) hit;
     
  30. Offline

    MistaKewl

    It worked! :D Thank you so much for your help and putting up with my noobi-ness (all of you). Means a lot! Thanks for helping me (even if I was acting like a retard at some points xD)


    @MistaKewl @Totom3 @Konato_K @mine-care


    + Followed
     
    mine-care and FisheyLP like this.
Thread Status:
Not open for further replies.

Share This Page