Solved A custom enchantment in an enchanting table

Discussion in 'Spigot Plugin Development' started by ZBLL, Jun 11, 2021 at 4:19 PM.

Thread Status:
Not open for further replies.
  1. The title says it all. I've got a properly registered enchantment and I really wish it could have appeared in an enchanting table. I've checked this https://www.spigotmc.org/threads/adding-custom-enchants-into-an-enchanting-table.378581/ but couldn't find a good answer - maybe I should take another look. Although I learnt from there that you should add a lore during an ItemEnchantEvent (which makes sense), I didn't really find a way to put my enchantment into an enchanting table. Is that even possible? If not, I am probably going to make a custom GUI to replace the default one. But if it is (even with NMS), please tell me how - I really hope to learn from this.
     
  2. So looks like he adds the enchantments by id.
    Code (Java):

    public void addCustomEnchantToTable(PrepareItemEnchantEvent e){
         
            if(e.getItem().getType() == Material.SLIME_BALL){
             e.setCancelled(false);
             e.getOffers()[0] = new EnchantmentOffer(new customEnchantment(92), 1, 1);
                e.getOffers()[1] = null;
                e.getOffers()[2] = new EnchantmentOffer(Enchantment.ARROW_FIRE, 1, 1);
         
         
            }
        }
     
    Well, I think that I can use it like this:
    Code (Java):

    public void addCustomEnchantToTable(PrepareItemEnchantEvent e){
         
            if(e.getItem().getType() == /* i'll check for a weapon */ ){
             e.setCancelled(false);
             if (Math.random() < 0.15) { /* 15% chance */
                 int offer = new Random().nextInt(3);
                 e.getOffers()[offer] = new EnchantmentOffer(MyMainClassThatAlreadyHasTheEnchantmentsAndItsIds.myEnchantment, 1, 1);
            }      
        }
    }
     
    This approach seems reasonable. May you take a look?
     
  3. Hey! It appears that 1.8.8 doesn't have this function... There's a public int[] getExpLevelCostsOffered() but I don't see how I can use this in a proper way.
     
    #5 ZBLL, Jun 12, 2021 at 1:16 PM
    Last edited: Jun 12, 2021 at 1:22 PM
  4. So I checked the JavaDocs for newer versions. Turns out that getExpLevelCostsOffered() is deprecated for us to use getOffers(), which means they are somehow connected, but I just don't see any connection between int[] which is required XP and EnchantmentOffer[] which are required offers, especially on 1.8.8.
     
  5. Sorry i didn't see your version. In 1.8.8, Inventory view can modify your xp, but I couldn't find anything about the enchants. I think you should use nms.
     
    • Useful Useful x 1
  6. I found ContainerEnchantTable in nms.
    This has a line of code
    Code (Java):
    this.h[j] = weightedrandomenchant.enchantment.id | weightedrandomenchant.level << 8;
    And maybe using reflection to modify the 'h' variable can do what you want.
     
    #8 Dolphin2410, Jun 13, 2021 at 2:55 AM
    Last edited: Jun 13, 2021 at 3:01 AM
    • Useful Useful x 1
  7. Listen to playerinteractEvent and cancel it if right click block and the block is an enchanting table.

    get the IInventory by
    Code (Java):
    ((CraftInventory) [blockClickedInventory]).getInventory()
    get the PlayerInventory by
    Code (Java):
    ((PlayerInventory) ((CraftInventory) e.player.getInventory()).getInventory())
    create a ContainerEnchantTable(PlayerInventory, NMSWorld, BlockPosition)

    set the container's 'h' variable to an array of your enchantment ids.

    call the 'a' method from the container. I think this calls prepareItemEnchantEvent with the modified enchants list

    I'm not sure, but hope this works :D
     
    • Useful Useful x 1
  8. Thanks! I am trying to implement this right now, and in the meantime could you tell me where do you find all of this? How do you understand what does an obfuscated method/variable do? Is there a documentation for NMS?
     
  9. So this is my code:
    Code (Java):

    @EventHandler
    public void interact(PlayerInteractEvent e) {
        if (e.getAction() == Action.RIGHT_CLICK_BLOCK) {
            if (e.getClickedBlock().getType() == Material.ENCHANTMENT_TABLE) {
                if (Math.random() < 0.15) {
                    IInventory iInventory = ((CraftInventory) e.getClickedBlock()).getInventory();
                    ContainerEnchantTable cet = new ContainerEnchantTable(
                            ((PlayerInventory) ((CraftInventory) e.getPlayer().getInventory()).getInventory()),
                            (net.minecraft.server.v1_8_R3.World) e.getPlayer().getWorld(),
                            new BlockPosition(e.getClickedBlock().getX(), e.getClickedBlock().getY(), e.getClickedBlock().getZ())
                    );
                    int offer = new Random().nextInt(3);
                    cet.h[offer] = armorEnchs[2].getId();
                    cet.a(iInventory);
                }
            }
        }
    }
     
    Even without testing it, I see two weird things. First of all, am I getting the IInventory correctly? I don't think I am. And secondly, with this approach I don't see a way to check what item is being prepared for an enchant, so I can't select a proper enchantment - what if it is a weapon and I am using a bow enchant? I see a way to add the player to a List, and on each PrepareItemEnchantEvent I should check if the player is in that list - if he is, I am committing the check for the enchantment. But it seems too complex
     
  10. Bump for message #11, since this thread has drowned in a bunch of other ones
     
  11. Yet another bump
     
  12. UPDATE: No errors in the console, thankfully. But I don't see my enchant in the table after enchanting like 40 helmets. I am pretty sure I am doing smth wrong. But I noticed that there are sometimes empty slots instead of enchantments, and idk if that's normal behavior or not...
     
  13. adding custom enchants to an enchantment table isn't very flexible.
    for example: when you add a custom enchant to an enchantment table, there's no way you can see the enchantment name while hovering over it, because it's handled by the client, and your custom enchant is unknown for him.
    and there is a limited slots number for it i assume too.
    those are simple 2 example reasons to not add custom enchants via the enchantment table GUI although it looks cool it won't be affective.
    overall it's possible but not flexible.
     
  14. just register your custom enchant "properly" and let the underlying Bukkit/NMS to handle including your custom enchants within the EnchantmentTable..
     
  15. what do you mean by "properly"? I've done all the necessary registration, the enchantment works, but it'll never appear in my table
     
  16. I doubt that there is no way for the client to see it - there's a method called "getName()" or something when you extend Enchantment, and I think they use exactly that to get the enchantment name
     
  17. But the names are „translated“ in the client to their „localized“ name and that will obviously be missing on the client :/
     
  18. ^. Even if you "properly" registered your custom enchant and the Enchantment table managed to select them, when that information goes to the client-side, there is no way for the client-side to decipher the name of the custom enchantment.
     
Thread Status:
Not open for further replies.