Solved Random enchantment trying to add level 0 enchant

Discussion in 'Spigot Plugin Development' started by NeverFlame42, Jul 11, 2021.

  1. First and for most, this isn't 100% my code. It's code from someone else modified for my needs. Here's the code:

    Code (Java):
    case CREEPER:
                   Material[] mats = new Material[]{Material.STONE_AXE, Material.STONE_SWORD, Material.STONE_PICKAXE};
                   Random rnd = new Random();
                   Material chosen = mats[rnd.nextInt(mats.length)];
                   ItemStack item = new ItemStack(chosen);
                   Location loc = event.getEntity().getLocation();
                   Arrays.stream(Enchantment.values())
                           .filter(ench -> ench.canEnchantItem(item))
                           .forEach(ench -> item.addEnchantment(ench, rnd.nextInt(ench.getMaxLevel()+1)));

                   loc.getWorld().dropItemNaturally(loc, item);
    What the code is meant to do is when I kill a creeper, it will drop a stone tool with random enchants for that tool, so you can't get protection on an axe. This piece of the code:
    Code (Java):
    .forEach(ench -> item.addEnchantment(ench, rnd.nextInt(ench.getMaxLevel()+1)));
    decides the level and if the level comes out as 0, the guy says it shouldn't apply it, but it does.

    Using this code causes nothing to spawn and errors to pop up in cmd. Using .addUnsafeEnchantments works, but then leaves stuff like Sharpness enchantment.level.0 in the lore, which I don't want.

    Any ideas? I'm probably overlooking something very obvious.
     
  2. The problem comes from the fact that you add 1 to the max level within random.nextInt. Change it to rnd.nextInt(ench.getMaxLevel()) + 1 to fix that.
     
    • Agree Agree x 1
  3. I tried that but then it applies all enchants that can be applied, including curses.
     
  4. That seems like it's caused by something else within your code then. random.nextInt(ench.getMaxLevel() + 1) will return a number between 0 and the max level. This is why you can get level 0 enchantments.
    The way you would resolve that is by using random.nextInt(ench.getMaxLevel()) + 1 as that would return a number between 1 and the max level.

    This should add random valid enchantments between 1 and the max level on the item.
    Code (Java):
     Arrays.stream(Enchantment.values())
    .filter(ench -> ench.canEnchantItem(item))
    .forEach(ench -> item.addEnchantment(ench, rnd.nextInt(ench.getMaxLevel()) + 1));
     
  5. It worked, but still gave every single enchant. With this code, I was hoping for something like when you find loot in an end city. A diamond chestplate usually only has a few enchants on it with various levels. It may not be possible with this setup.
     
  6. I feel silly for not seeing why that happens earlier. It basically will give every enchantment because you never randomize which enchantments it gives. It will always apply all valid enchantments for that item. You have to create a randomizer to give a chance to applying each enchantment.
    I'm not too knowledgeable about Arrays.stream and don't see an obvious way to randomize using it, so I would personally use a solution such as this:
    Code (Java):
    for(Enchantment ench : Enchantment.values()) {
                if(ench.canEnchantItem(item) && rnd.nextBoolean()) {
                    item.addEnchantment(ench, rnd.nextInt(ench.getMaxLevel()) + 1);
                }
            }
    This gives a 50% chance for the enchantment to be applied and the level will be between 1 and the max level.
     
    • Winner Winner x 1
  7. Oh my god, you're the true MVP, thank you so much. It didn't help that I'm like you, Arrays.streams is like a foreign object I was poking with a stick lmao. Thanks again dude, you really helped me out.