Util [Class] Custom Vilager's trade items!

Discussion in 'Resources' started by mine-care, Jan 29, 2015.

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

    mine-care

    I have seen this question in the forums at random and i liked it as a feature so i decided to try it out myself and add it to my kitpvp server and since i made it, i decided to make a more "flexible" version of it and give it out to all guys who are willing to use it =D.

    Notes:
    You can set UP TO 5 custom trades, (thats the max vilagers accept), less will cause villager to generate the rest by itself. Forget this! my fault!

    The class itself:
    Code (open)

    Code:
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import org.bukkit.Bukkit;
    import org.bukkit.entity.Villager;
    import org.bukkit.inventory.ItemStack;
    
    public class CustomVillagerV2 {
        private Object ev;
        private Object list;
        private static final String bukkitversion = Bukkit.getServer().getClass()
                .getPackage().getName().substring(23);
    
        public CustomVillagerV2(Villager villager) {
            try {
                Class<?> merchantrlist = Class.forName("net.minecraft.server."
                        + bukkitversion + ".MerchantRecipeList");
                list = merchantrlist.newInstance();
                Class<?> craftvillager = Class.forName("org.bukkit.craftbukkit."
                        + bukkitversion + ".entity.CraftVillager");
                Method handle = craftvillager.getMethod("getHandle", null);
                ev = handle.invoke(craftvillager.cast(villager), null);
            } catch (NoSuchMethodException | SecurityException
                    | ClassNotFoundException | IllegalAccessException
                    | IllegalArgumentException | InvocationTargetException
                    | InstantiationException e) {
                e.printStackTrace();
            }
        }
    
        public CustomVillagerV2 addRecipe(ItemStack slotOne, ItemStack slotTwo,
                ItemStack output) {
            try {
                Class<?> mechantRecipe = Class.forName("net.minecraft.server."+bukkitversion+".MerchantRecipe");
                Method add = list.getClass().getDeclaredMethod("a", mechantRecipe);
                Class<?> nmsItemStack = Class.forName("net.minecraft.server."
                        + bukkitversion + ".ItemStack");
                Constructor<?> merchantRecipeConstructor = mechantRecipe.getDeclaredConstructor(nmsItemStack,nmsItemStack,nmsItemStack);
                Object merchantRecipeObj = merchantRecipeConstructor.newInstance(toNMSItemStack(slotOne), toNMSItemStack(slotTwo),toNMSItemStack(output));
                add.invoke(list,merchantRecipeObj);
            } catch (ClassNotFoundException | NoSuchMethodException
                    | SecurityException | IllegalAccessException
                    | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
                e.printStackTrace();
            }
            return this;
        }
    
        public CustomVillagerV2 addRecipe(ItemStack slotOne, ItemStack output) {
            try {
                Class<?> mechantRecipe = Class.forName("net.minecraft.server."+bukkitversion+".MerchantRecipe");
                Method add = list.getClass().getDeclaredMethod("a", mechantRecipe);
                Class<?> nmsItemStack = Class.forName("net.minecraft.server."
                        + bukkitversion + ".ItemStack");
                Constructor<?> merchantRecipeConstructor = mechantRecipe.getDeclaredConstructor(nmsItemStack,nmsItemStack);
                Object merchantRecipeObj = merchantRecipeConstructor.newInstance(toNMSItemStack(slotOne),toNMSItemStack(output));
                add.invoke(list,merchantRecipeObj);
            } catch (ClassNotFoundException | NoSuchMethodException
                    | SecurityException | IllegalAccessException
                    | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
                e.printStackTrace();
            }
            return this;
        }
    
        private Object toNMSItemStack(ItemStack i) {
            try {
                Class<?> craftItemstack = Class.forName("org.bukkit.craftbukkit."
                        + bukkitversion + ".inventory.CraftItemStack");
                Method nmsCopy = craftItemstack.getDeclaredMethod("asNMSCopy", ItemStack.class);
                return nmsCopy.invoke(craftItemstack, i);
            } catch (ClassNotFoundException | NoSuchMethodException
                    | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public boolean finish() {
            try {
                Field f = ev.getClass().getDeclaredField("bu");
                f.setAccessible(true);
                f.set(ev, list);
                return true;
            } catch (NoSuchFieldException | SecurityException
                    | IllegalArgumentException | IllegalAccessException e) {
                return false;
            }
        }
    }
    



    Example usage:
    Code:
    public class Main extends JavaPlugin implements Listener {
    
        public void onEnable(){
            getServer().getPluginManager().registerEvents(this, this);
        }
    
        @EventHandler
        public void sneak(final PlayerToggleSneakEvent e) {
            if (!e.isSneaking())return; //We dont want the event to fire twice and spawn 2 vilagers/
            Player p = e.getPlayer();
            ItemStack i = new ItemStack(Material.EMERALD, 3); //Creating the  input itemstack
            ItemStack out = new ItemStack(Material.PAPER,2); //Creating output itemstack
            ItemMeta im = out.getItemMeta();//adding some fancy meta :3
            im.setDisplayName(ChatColor.GREEN+"Special Paper");
            im.setLore(Arrays.asList(ChatColor.GOLD+"Magic paper! WOOSH!"));
            out.setItemMeta(im);
            //Spawning a vilager
            Villager vil = (Villager) p.getLocation().getWorld().spawnEntity(p.getLocation(), EntityType.VILLAGER);
            //Creating a CustomVilager instance to modify spawned villager
            new CustomVillagerV2(vil).addRecipe(i,out).finish();
            //adding a recipe, and finishing.
        }
    }
    Available Methods:
    addRecipe(ItemStack slot1, ItemStack slot2, ItemStack output)
    This method adds a recipe that requires 2 inputs for one output. (see how enchanter vilagers work)

    addRecipe(ItemStack slot1, ItemStack slot2, ItemStack output)
    This method is the simple side of the one above, it rewuires one input for it to give output.

    finish()
    This method MUST be called when you are done and you have added recipes, it also returns boolean indicating if it was successful or not (false = error occured)

    Bugs & TODO:
    1. Customise max ammount of custom items, up to now vilagers have a trade limit.
    2. Stop vilagers from thinking of new recipes and offers because if someone purchases the custom one, they generate a random one. <Help :-(>
    3. Recipes added should differ from one-another.
    4. Add javadoc

    Please let me know your sugestions/ideas or any corrections i should do.
    Also if you know how to fix any of the above bugs (Especially 2) Please give me a shout.
    Ty <3
     
    Last edited: Jan 30, 2015
    leon3001, Totom3 and ChipDev like this.
  2. Offline

    ChipDev

    i wuv it
     
    mine-care likes this.
  3. Online

    timtower Administrator Administrator Moderator

  4. Offline

    mine-care

    ChipDev likes this.
  5. Offline

    MiniDigger

    I am not quite sure but this could either be caused by not setting the maxUses field in MerchantRecipe or by not setting the career of the villager to something useless.
    This is how I do it ( I think I copied to code from ShopKeepers, not quite sure, so don't credit me for that ;D)
    http://paste.minidigger.me:7777/yehizugota.avrasm
     
    mine-care likes this.
  6. Offline

    mine-care

    @MiniDigger :-O first of all thanks for your reply, secondly thanks for your walk through (or stowaway pointing it out =D) I will try to implement It! And thirdly and most importantly I think, thanks for being honest! I really apriciate honest people <3
     
  7. Offline

    MiniDigger

    @mine-care If you got any problems just feel free to pm me. I spend quite some time in nms code, trying to figure out how stuff works ;D
     
  8. Offline

    ProStriker123

    new CustomVillagerV2(vil).addRecipe(is,outs).addRecipe(i,out).finish();
    Why its dosent adds me to pages in the villager?
     
  9. Offline

    mine-care

    @ProStriker123 Please provide:
    your server's version
    a detailed description
    is there a error thrown?
    Thanks.
     
  10. Offline

    ProStriker123

  11. Offline

    mine-care

  12. Offline

    sgavster

    I'm a little confused; sorry if this question is just stupid:

    Will this just make it when you shift it spawns a villager? I don't think it does, but I am just confused lol.

    Also, would this work with custom villagers, like say I wanted to make a command

    /customizevillager <tradefor1,tradefor2> <whatyouget>

    So when you look at a villager, it will give that villager that trade? I have no idea, just wondering if that works with this code.

    Sorry again for this stupid question.

    Thanks!
     
  13. Offline

    mine-care

    @sgavster there are no stupid questions, only stupid answers :- ) (Hopefully i am not providing one...)
    Youre right... i havent included a proper documentation. Essentially you spawn a vilager (normally with bukkit.spawnEntity or whatever), and then you can do new CustomVillagerV2(villagerObj); where villagerObj is the spawned villager, Then you can use the methods to add trades! :- ) and when you are done with adding thrades, you call method finish(); to finish the job :p When you call the finish() method it will automatically set the trades to the villager that is spawned, you dont need to take further action =D And yes you can make a plugin with the command above :) nice idea btw!
    You can folow the example code above and play with it a litle bit till you get used to it.
    If you need further help, dont hesitate to ask, i am all ears!
     
    sgavster likes this.
  14. Offline

    sgavster

    @mine-care Oh alright, that makes a lot more sense.

    I'm really not sure how to spawn a custom villager through bukkit, is there a good tutorial? So if I spawned the villager, could I make it editable (getTargetCreature, or something)? Sorry, again, I haven't done this in a while, and thanks for the help!
     
  15. Offline

    mine-care

    Well with this api you don't need to spawn a custom Villager, simply: Villager v = (Villager)Bukkit.spawnEntity(location where , EntityType.VILLAGER ); and then customVillagerV2 c = new CustomVillagerV2(v);
    The villager is spawned already by the line (Bukkit.spawnEntity...) :ยท)
     
Thread Status:
Not open for further replies.

Share This Page