Solved Make player throw a potion

Discussion in 'Spigot Plugin Development' started by Xatlor, May 18, 2016.

  1. Hello Forum,
    I want players to be able to throw a poison potion by right-clicking a pufferfish.
    Code (Text):
        @EventHandler
        public void onInteract(PlayerInteractEvent e) {
            if (e.getAction().equals(Action.RIGHT_CLICK_AIR) || e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
                if (e.getPlayer().getItemInHand().getType().equals(Material.RAW_FISH) && e.getPlayer().getItemInHand().getDurability() == 3) {
                    ThrownPotion tp = e.getPlayer().launchProjectile(ThrownPotion.class);
                    tp.getEffects().add(new PotionEffect(PotionEffectType.POISON, 33, 0));
                   
                    ItemStack is = e.getPlayer().getItemInHand();
                    is.setAmount(is.getAmount()-1);
                    e.getPlayer().setItemInHand(is);
                    e.getPlayer().updateInventory();
                }
            }
        }
    First, the potion is not poisonous, but just a regular water bottle.

    Second, there is some really strange behaviour with the amount-consume:
    - Right-clicking air or blocks while having more than 1 pufferfish will reduce its amount by 1
    - Right-clicking air while having 1 pufferfish will not reduce the amount, but throw a water bottle
    - Right-clicking blocks while having 1 pufferfish will consume it

    Here, take this video to understand what I mean:


    Thanks for your help!
     
  2. You're trying to add potion effects to an entity, not a potion... You need to change the potion type.
     
  3. Well after some testing this is my final result on how I implemented what you wanted into my own code. I hope this clears up any of the weird behavior that you've been having and you'll learn from my explanation and understand how to implement this into your own code. I have to say that the method that you were trying to use

    [TESTED]

    Code (Text):
    ThrownPotion tp = e.getPlayer().launchProjectile(ThrownPotion.class);
                    tp.getEffects().add(new PotionEffect(PotionEffectType.POISON, 33, 0));
                   
    seems to fail and not work at all in my case, so I resulted in using MetaDataValues and some basic knowledge.

    So here's my way of doing this:

    Code (Text):
    @EventHandler
        public void onClick(PlayerInteractEvent event) {

            // Checking the action, pretty sure player interact event fires 20 times just by moving your mouse around
            if (event.getAction().equals(Action.RIGHT_CLICK_AIR) || event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
                ItemStack stack = event.getPlayer().getItemInHand();

                // If the item is null or the player isn't clicking anything do not continue
                if (stack == null || stack.getType().equals(Material.AIR)) {
                    return;
                }

                // Check to make sure what the player is clicking is a puffer fish
                if (stack.getType().equals(Material.RAW_FISH) && stack.getDurability() == 3) {

                    // Cancelling the event because if you spam this you actually eat the puffer fish giving you poison 4 and a few other effects
                    event.setCancelled(true);

                    // Working on finding what's left of the stack before hand
                    int amount = stack.getAmount();

                    // Remove the item if it's the last pufferfish
                    if (amount == 1) {
                        event.getPlayer().setItemInHand(null);
                    } else {
                        // Set the amount to 1 less as they used 1
                        event.getPlayer().getItemInHand().setAmount(amount - 1);
                    }


                    // Make the thrown potion effect (replicating what I saw in your video assuming this is what you would have desired)
                    ThrownPotion potion = event.getPlayer().launchProjectile(ThrownPotion.class);
                    // This is where things get complicated because the ThrownPotion#getEffects() failed to work in any of my tests
                    // So I resulted in giving the entity a FixedMetaDataValue with all the respective information required for the effect
                    potion.setMetadata("EffectData", new FixedMetadataValue(this, "POISON;33;0"));
                }
            }
        }

        // PotionSpawnEvent is called when the potion splashes (doh)
        @EventHandler
        public void onPotionSplash(PotionSplashEvent event) {

            // We check if the entity has the identifier that we set above to make sure that we can gather the appropriate details from it
            if (event.getEntity().hasMetadata("EffectData")) {
                List<MetadataValue> metadata = event.getEntity().getMetadata("EffectData");

                // This is the string containing all the data/information that was given to the entity above
                String details = metadata.get(0).asString();
                String[] array = details.split(";");

                // Just a extra check to make sure we can gather all the information about the potion effects
                if (array.length == 3) {
                 
                    // Gathering all the information the effectType, duration and amplifier
                    PotionEffectType type = PotionEffectType.getByName(array[0]);
                    int duration = Integer.parseInt(array[1]) * 20;
                    int amplifier = Integer.parseInt(array[2]);

                    // Get all the entities affected by the potion splash and give them the potion effect that we wanted above
                    for (LivingEntity entity : event.getAffectedEntities()) {
                        entity.addPotionEffect(new PotionEffect(type, duration, amplifier));
                    }
                    // ???
                    // Profit?
                }
             
                // Dispose of the EffectData MetaData (good practice)
                event.getEntity().removeMetadata("EffectData", this);
            }
        }
     
    #3 frostythedev, May 18, 2016
    Last edited: May 18, 2016
    • Informative Informative x 1
  4. Thank you for helping. I found a simpler solution tho, if anyone wants to see it:
    Code (Text):
    e.setCancelled(true); // Cancel to fix the comsume-bug

    ThrownPotion tp = e.getPlayer().launchProjectile(ThrownPotion.class);
    tp.setItem(new ItemStack(Material.POTION, 1, (short) 16388));

    int amount = e.getPlayer().getItemInHand().getAmount();
    if (amount == 1) {
        e.getPlayer().setItemInHand(new ItemStack(Material.AIR));
    } else {
        e.getPlayer().getItemInHand().setAmount(amount-1);
    }
    e.getPlayer().updateInventory();