Solved Using HashMap w/ Multiple Value Types

Discussion in 'Plugin Development' started by Wheaties66613, Mar 12, 2021.

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

    Wheaties66613

    Hello everyone.
    I'm trying to code a plugin I saw in a TapL video for personal use, and I'm stuck on an issue. (Plugin = whenever your character physically moves, a random item in your inventory gets a random enchantment. If the item already has said enchantment, it will add +1 level). The plugin technically works, but the level system isn't exactly how I would like it. It's a bit hard to explain, stick with me.

    The plugin will add new enchants fine, and adding the level works, but it will add levels based on the maximum level already applied to the item to a previous enchantment. Ex: sword has Sharpness 1, Looting 1, and Aqua Affinity 1. Plugin adds Sharpness to sword, it becomes Sharpness 2. Plugin adds Aqua Affinity, it becomes Aqua Affinity 3. Plugin adds Looting to sword, it becomes Looting 4, etc. Ideally it would add Aqua Affinity 2 and Looting 2. This would also apply to all of the other items in the inventory, so if my helmet has Fire Aspect 1, and the plugin added Fire Aspect again, it would become Fire Aspect 5.

    Here is the code for the plugin right now, with the issue (rest of plugin is very straightforward, I'll just put it all in):
    Code:
    package io.github.wheaties66613;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Random;
    
    import org.bukkit.enchantments.Enchantment;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.player.PlayerMoveEvent;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.PlayerInventory;
    import org.bukkit.plugin.java.JavaPlugin;
    
    public class Core extends JavaPlugin implements Listener {
    
        @Override
        public void onEnable() {
            for(Enchantment e : Enchantment.values()) {
                eList.add(e);
            }
            this.getServer().getPluginManager().registerEvents(this, this);
        }
     
        @Override
        public void onDisable() {
         
        }
     
        public List<Enchantment> eList = new ArrayList<>(); //needed
     
        private HashMap<Enchantment, Integer> enchMap =
                new HashMap<Enchantment, Integer>();
     
        @EventHandler
        public void onWalk(PlayerMoveEvent e) {
            if(e.getFrom().equals(e.getTo())) {
                return;
            }
            Enchantment randE = eList.get(new Random().nextInt(eList.size()));
            PlayerInventory inv = e.getPlayer().getInventory();
            ItemStack i = inv.getContents()[new Random().nextInt(inv.getContents().length - 1)];
            if(i != null) {
                if(!(enchMap.containsKey(randE))) {
                    i.addUnsafeEnchantment(randE, 1);
                    enchMap.put(randE, 1);
                    return;
                }
                int lvl = i.getEnchantmentLevel(randE);
                i.addUnsafeEnchantment(randE, lvl + 1);
                enchMap.replace(randE, lvl, lvl + 1);
            }  
        }
     
     
    }
    
    Going back to the plugin advice, I had the idea to essentially add an additional parameter to the hashmap, the item, to isolate the enchantments applied to that item, hopefully preventing the enchantment levels being applied to all items in the inventory. I'll keep working on the too-high enchantment levels. Obviously, hashmaps can only have two generics. I've tried multiple solutions, including wrapper classes, tuples, arraylists, HashMultiplemaps, Map.Entry, but I still cannot solve it for the life of me. Any help would be appreciated. No idea if this is a beginner fix, so sorry if that is the case.

    Also I wrote this from memory on my laptop, I have the plugin already coded on my desktop. Sorry if it's messy or has errors.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited: Mar 13, 2021
  2. Offline

    CraftCreeper6

    @Wheaties66613
    So you want to have a list of enchantments that are specific to one item type?
     
  3. Offline

    Wheaties66613

    Ideally. My initial idea was to have a HashMap that stored an ItemStack as the key, and both the Enchantment and the level(Integer) assigned to it. That might not be the best way to do it, idk. The problem is I can't figure out how to implement that, given that HashMaps only allow for two data types.
     
  4. Offline

    Strahan

    You can do a multidimensional map or, what I'd do, make a custom data object to hold the information.

    Also you should be declaring that Map<Enchantment, Integer> enchMap not HashMap<Enchantment, Integer> enchMap per SOLID design principles (Liskov Substitution Principle).

    Also also, your map is singular. So it will apply to everyone. It really should be keyed to the player via their UUID which would lead again to just making a custom object. It'd be far easier that way IMO.

    Lastly, why track it in the map at all? Evaluate the enchants on the item and just increment the stored ench level of that particular enchant if you are incrementing an existing vs adding a new one.

    EDIT: It sounded intriguing, so I wrote some code where if I walk over a diamond block, any item in my hand will get a random enchantment applied. If the random enchant already exists on the item, it will increment the level of the enchant by one unless it hits max enchant level. If I were doing this for real, I'd add a config backend to allow for configuration of what enchants are available and setting max levels in case I want to override the built in maxes (like allow sharpness 20 or something heh) and setup permissions and a cooldown and such but as this is just a "proof of concept" I didn't go down that rabbit hole heh.

    Code:
    @EventHandler
    public void onPME(PlayerMoveEvent e) {
      if (e.getFrom().getBlockX() == e.getTo().getBlockX() &&
          e.getFrom().getBlockY() == e.getTo().getBlockY() &&
          e.getFrom().getBlockZ() == e.getTo().getBlockZ()) return;
      if (e.getTo().getBlock().getRelative(BlockFace.DOWN).getType() != Material.DIAMOND_BLOCK) return;
    
      Enchantment ench = getRandomEnchant(e.getPlayer());
      if (ench == null) return;
    
      ItemStack upgradedItem = incrementEnchant(ench, e.getPlayer().getInventory().getItemInMainHand());
      e.getPlayer().getInventory().setItemInMainHand(upgradedItem);
    }
    
    public ItemStack incrementEnchant(Enchantment ench, ItemStack i) {
      int[] enchLevel = {1};
      ItemMeta im = i.getItemMeta();
    
      im.getEnchants().entrySet().forEach(e -> { if (e.getKey() == ench) enchLevel[0] = e.getValue()+1; } );
      if (enchLevel[0] > ench.getMaxLevel()) enchLevel[0] = ench.getMaxLevel();
    
      im.addEnchant(ench, enchLevel[0], true);
      i.setItemMeta(im);
      return i;
    }
     
    Last edited: Mar 12, 2021
    Wheaties66613 likes this.
Thread Status:
Not open for further replies.

Share This Page