Binding action to tool

Discussion in 'Plugin Development' started by ArsenArsen, Jul 10, 2014.

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

    ArsenArsen

    Hello, i am wondering how to bind action to tool like WorldEdit or PowerTool did, and how to apply it to itemstack i am giving using command, and making that itemstack recipe.
     
  2. Offline

    dsouzamatt

    ArsenArsen Do you mean creating something like the WE wand, where if you use the item it performs a special action? Please try to be more specific.
     
  3. You could have a class for ItemData:
    Code:
    public class ItemData {
        private Material material = null;
        private short materialData = (short) 0;
        private String itemName = null;
        private List<String> itemLore = null;
     
        public ItemData(Material material) {
            this.material = material;
        }
     
        public ItemData(Material material, short materialData) {
            this.material = material;
            this.materialData = materialData;
        }
     
        public short getData() {
            return this.materialData;
        }
     
        public List<Strnig> getLore() {
            return this.itemLore;
        }
     
        public Material getMaterial() {
            return this.material;
        }
     
        public String getName() {
            return this.itemName;
        }
     
        public boolean hasName() {
            return this.itemName != null;
        }
     
        public boolean hasLore() {
            return this.itemLore != null;
        }
     
        public ItemData setName(String itemName) {
            this.itemName = itemName;
            return this;
        }
     
        public ItemData setLore(List<String> lore) {
            this.itemLore = lore;
            return this;
        }
    }
    
    Then have:
    Code:
    Map<ItemData, List<String>> itemsCommands = new HashMap<ItemData, List<String>>();
    The ItemData is the item.
    The List<String> is the list of commands.

    Then, listen to PlayerInteractEvent:
    Code:
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getAction() == Action.RIGHT_CLICK_AIR) {
            if (event.getItem() != null) {
                List<String> commands = this.getCommands(event.getItem());
                if (commands != null) {
                    for (String command : commands) event.getPlayer().chat("/" + command);
                } 
            }
        }
    }
     
    public List<String> getCommands(ItemStack itemStack) {
        if (itemStack != null) {
            for (Map.Entry<ItemData, List<String>> itemEntry : plugin.itemCommands.entrySet()) {
                ItemData itemData = itemEntry.getKey();
                if (itemStack.getType() == itemData.getMaterial() && (itemStack.getType().isBlock()  ? itemStack.getDurability() == itemData.getData() : true)) {
                   if (itemData.hasName() || itemData.hasLore()) {
                        ItemMeta itemMeta = itemStack.getItemMeta();
                        if (itemMeta != null) {
                            if (itemMeta.hasDisplayName() && itemMeta.hasLore()) {
                                if (itemData.hasName() && itemData.hasLore()) {
                                    if (itemData.getName().equals(itemMeta.getDisplayName())) && itemData.getLore().equals(itemMeta.getLore()) return itemEntry.getValue();
                                }
                            } else if (itemMeta.hasDisplayName()) {
                                if (itemData.hasName() && !itemData.getLore()) {
                                    if (itemData.getName().equals(itemMeta.getDisplayName())) return itemEntry.getValue();
                                }
                            } else if (itemMeta.hasLore()) {
                                if (itemData.hasLore() && !itemData.hasName()) {
                                    if (itemData.getLore().equals(itemMeta.getLore())) return itemEntry.getValue();
                                }
                            }
                        }
                    } else {
                        return itemEntry.getValue();
                    }
                }
            }
        }
        return null;
    }
    
     
  4. dsouzamatt
    He probably means that you can bind a command to an ItemStack, and when you left click with the said ItemStack, the command will be executed by you.
     
  5. Offline

    ArsenArsen

    Exactly.
    I meant to make crafting recipe with result of that custom itemstack. Witch perform action. And action is loading schematic.

    Itemlore is undefined

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  6. See setLore() method? It returns ItemData, so you can do: new ItemData(type, data).setLore() etc.
    The reason being, lore's not necessary and so is item name. Btw, I'm still editing it.

    Done. However, just when I was finished, I realized you could easily just do:
    Code:
    public Map<Map<String, Object>, List<String>> itemsCommands = new HashMap<Map<String, Object>, List<String>>();
     
    public void addItem(ItemStack item, List<String> commands) {
        this.itemCommands.put(item.serialize(), commands);
    }
    
    Code:
    public List<String> getCommands(ItemStack itemStack) {
        return itemStack != null ? plugin.itemCommands.get(itemStack.serialize()) : null;
    }
    
    I think this is much more efficient and should work too.

    So, when creating a custom recipe, make sure the result ItemStack has all the attributes the real item binded should have.

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

    ArsenArsen

    To put it to EventLstener or to Main class?
    One more thing, if i put getMaterial to recipe is it going to work?
     
  8. You put the getCommands() and onPlayerInteract() code in the EventListener, and the Map (+ addItem() if you want) in the Main class.

    If you put getMaterial() to recipe? What do you mean...?
     
  9. Offline

    ArsenArsen

    Nvm for recipe, not gonna do it(bad idea)\
    On Listener has :
    Code:
    public List<String> getCommands(ItemStack itemStack) {
        return itemStack != null ? plugin.itemCommands.get(itemStack.serialize()) : null;
    }
    right? And onPlayerIteract has getCommand?
     
  10. Yes, onPlayerInteract() and getCommands() should be in the EventListener class, and onPlayerInteract() calls getCommands().
     
    ArsenArsen likes this.
  11. Offline

    Eelek

    Probaly you shoud use: (Just an idea, but...)

    Code:java
    1. //For binding it, use a command:
    2. public boolean onCommand(CommandSender sender, Command cmd, String commandlabel, String[] args){
    3. if(cmd.getName().equalsIgnoreCase("bind"){
    4. ItemStack item = p.getItemInHand();
    5. ItemMeta im = (ItemMeta) item.getItemMeta();
    6. if(args[0].equalsIgnoreCase(args[0]){
    7. im.setLore(args[0]);
    8. Material m = item.getType();
    9. im.setDisplayeName(ChatColor.GREEN + m);
    10. item.setItemMeta(im);
    11. }
    12. }
    13. return false;
    14.  
    15. //Now you need to use an EventHandler for performing the command
    16. @EventHandler
    17. public void onClick(PlayerInteractEvent e){
    18. Player p = e.getPlayer()
    19. ItemStack item = p.getItemInHand()
    20. ItemMeta im = (ItemMeta) item.getItemMeta();
    21. Material m = item.getType();
    22. if(e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.LEFT_CLICK_BLOCK){
    23. if(item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + m){
    24. p.performCommand(im.getLore());
    25. }
    26. }


    If you have any errors, please reply!
     

  12. Oh my god Eelek - are you a half-troll? Everything's wrong.

    • You returned false OUT of the boolean method.
    • You never returned true if the command is bind.
    • You never checked if the args length is greater than 0.
    • You never checked if the item-in-hand is null.
    • You checked if args[0] is equal to itself?
    • The object "p" doesn't even exist.
    • You didn't check if the sender is a CommandSender.
    • You missed 2 semicolons at the end of the first 2 lines of code in onClick().
    • Why would you set the display name of the item to a Material in green? That'd look very ugly, having something like: &aDIAMOND_SWORD
    • You never checked if the item is null in onClick().
    • You never checked if the item has an item meta.
    • You never checked if the item has a display name.
    • You missed out a } in the last if() statement.
    • You called item.getItemMeta() again yet you already had an ItemMeta object (im).
    • The method "performCommand()" only accepts a String, not a List<String>.
    This list can go on. Please don't give terrible, incorrect and buggy as F*** code.
     
    ArsenArsen likes this.
  13. Offline

    ArsenArsen

    I did NOT meant do bind regular command, i wanted to bind loadArea void to it, like
    Code:java
    1. public void loadArea(World world, File file, Vector origin) throws DataException, IOException, MaxChangedBlocksException {
    2. EditSession es = new EditSession(new BukkitWorld((org.bukkit.World) world), 999999999);
    3. CuboidClipboard cc = CuboidClipboard.loadSchematic(file);
    4. cc.paste(es, origin, false);
    5.  
    6. }

    Code:java
    1. public class EventListener extends Main implements Listener {
    2.  
    3. public List<String> getCommands(ItemStack itemStack) {
    4. return itemStack != null ? this.itemsCommands.get(itemStack.serialize()) : null;
    5. }
    6.  
    7. @EventHandler
    8. public void onPlayerInteract(PlayerInteractEvent rce){
    9. if(rce.getAction() == Action.RIGHT_CLICK_BLOCK){
    10. try {
    11. loadArea(world, tent, ONE );
    12. } catch (MaxChangedBlocksException | DataException | IOException e) {
    13. e.printStackTrace();
    14. }
    15. }
    16.  
    17. }
    18.  
    19. }
    20.  


    KingFaris11 should i do this way?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  14. Question: Why does your EventListener extends Main? You do realize this means that you're creating another instance of a plugin when calling new EventListener()?

    Either use JavaPlugin.getPlugin(Main.class) to get methods inside Main or use something like this method (I don't remember generic methods by heart):
    Code:
    public <T extends JavaPlugin> T getPlugin(String pluginName) {
         return (T) Bukkit.getPluginManager().getPlugin(pluginName);
    }
    
    Do not do his way at all.

    In terms of my way and what you want, you could make a command that calls loadArea(), and for the binding of the item, you could bind the item stack with the load area command.

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

    ArsenArsen

    I know i meant my way, i puttet code above, his code is superbuggy.

    ItemsComand needs to be static if i unlink.
    JavaPlugin.getPlugin(Main.class) works

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  16. Don't make itemsCommand static firstly. Use JavaPlugin.getPlugin(Main.class).itemsCommands or make a plugin instance (static) to access it (make sure itemsCommands is public).

    Also, your way is half correct. Look at what I said previously on how you should do it.

    Put this in your EventListener:
    Code:
    public void addItem(ItemStack item, List<String> commands) {
        JavaPlugin.getPlugin(Main.class).itemsCommands.put(item.serialize(), commands);
    }
    
    Let's say your command for loading the area is "/loadarea", to register it, you can do:
    Code:
    addItem(item, Arrays.asList("loadarea"));
    
    onPlayerInteract():
    Code:
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
            if (event.getItem() != null) {
                List<String> commands = this.getCommands(event.getItem());
                if (commands != null) {
                    for (String command : commands) event.getPlayer().performCommand(command);
                }
            }
        }
    }
    
     
  17. Offline

    ArsenArsen

    public void onPlayerInteract(PlayerInteractEvent rce){
    if(rce.getAction() == Action.RIGHT_CLICK_BLOCK){
    try {
    Main.loadArea(Main.world, Main.tent, Main.ONE );
    } catch (MaxChangedBlocksException | DataException | IOException e) {
    e.printStackTrace();
    }
    }
    I want item to load area on right click(code i have is above)
     
  18. So what's the point of having all of this if you just want to call loadArea() when someone right clicks a block?
     
  19. Offline

    ArsenArsen

    I want to call it if it is specific item. My code was wrong, i want to call if it is right-clicked with binded item.

    Ok, i give up unless anyone has suggestion.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  20. Is this "Specific item" changeable, and the only item that can have a bind?
     
  21. Offline

    ArsenArsen

    Yea, like //tree can bind to only one diamond sword, not to all diamond swords at same time.
     
  22. Offline

    Necrodoom

    ArsenArsen bumping is allowed only every 24 hours, not 15 minutes. Your thread has been also posted 4 hours ago, have some patience.
     
    ArsenArsen likes this.
  23. I don't quite get you, sorry, can't help.
     
  24. Offline

    ArsenArsen

    WorldEdit has //tree command, it automaticly binds to item in hand. I just want to skip checking item in hand ( pratcicly a //wand command) and to get item from config using Material.getMaterial().

    - bump -

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

    fireblast709

    KingFaris11 you lack a hashCode() method for the Map key and don't suggest static instances
     
  26. Why would I need a hashCode() method for the Map key? What? A hashCode() represents just the class and not the instance, does it not? So it'll always be the same...? Also, I can suggest static instances. I also said to use JavaPlugin.getPlugin(Main.class) instead first.
     
  27. Offline

    fireblast709

    KingFaris11 hashCodes are value based, and are used in HashMaps for more equal distribution in the internal hashtable
     
  28. I've never learnt about this, can you please give a small example in a private message?
     
Thread Status:
Not open for further replies.

Share This Page