Util CSGO Crates: Create inventorys the cycle through items.

Discussion in 'Resources' started by Zombie_Striker, Sep 17, 2016.

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

    Zombie_Striker

    Description:
    This is a util that replicates CSGO Crates. With this, items inside the inventory will cycle through for a specified amount of seconds before it stops and gives the item to the player.

    Code:

    Code:
    
    import java.util.concurrent.ThreadLocalRandom;
    
    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;
    import org.bukkit.event.*;
    import org.bukkit.event.inventory.InventoryClickEvent;
    import org.bukkit.inventory.*;
    import org.bukkit.plugin.Plugin;
    import org.bukkit.scheduler.BukkitRunnable;
    
    /**
     * Created by Zombie_Striker.
     * https://bukkit.org/threads/csgo-crates-create-inventorys
     * -the-cycle-through-items.431949
     */
    
    public class CrateInventory {
    
       private Inventory inv;
       private Plugin plugin;
       private ItemStack[] contents;
    
       private InventoryClickHandler helper;
    
       private int itemIndex = 0;
    
       public CrateInventory(int size, ItemStack[] contents, String name,
           Plugin main) {
         inv = Bukkit.createInventory(null, size, name);
         this.plugin = main;
         this.contents = contents;
         helper = new InventoryClickHandler(main, name);
         shuffle();
       }
    
       /**
        * Shuffles the existing inventory.
        *
        * Note that this does not shuffle the order of the items. This only shuffles the starting index.
        */
       public void shuffle() {
         int startingIndex = ThreadLocalRandom.current().nextInt(
             this.contents.length);
         for (int index = 0; index < startingIndex; index++) {
           for (int itemstacks = 0; itemstacks < inv.getSize(); itemstacks++) {
             inv.setItem(itemstacks, contents[(itemstacks + itemIndex)
                 % contents.length]);
           }
           itemIndex++;
         }
       }
    
       /**
        * Returns the inventory click manager. There should be no reason why you
        * need it unless you want to unregister the class (meaning players can pick
        * up items)
        *
        * @return
        */
       public InventoryClickHandler getInventoryClickManager() {
         return helper;
       }
    
       /**
        * Sets the contents for the crate.
        *
        * @param contents
        */
       public void setContents(ItemStack[] contents) {
         this.contents = contents;
       }
    
       /**
        * Returns the contents of the crate
        *
        * @return The contents of the crate
        */
       public ItemStack[] getContents() {
         return this.contents;
       }
    
       /**
        * Returns the inventory instance
        *
        * @return The inventory instance.
        */
       public Inventory getInventory() {
         return inv;
       }
    
       /**
        * This spins the inventory. Call this to play the animation and give a
        * random item.
        *
        * @param seconds
        *  the amount of second you want this to spin.
        * @param reciever
        *  player who should recieve the item.
        */
       public void spin(final double seconds, final Player reciever) {
         reciever.openInventory(this.inv);
         new BukkitRunnable() {
           double delay = 0;
           int ticks = 0;
           boolean done = false;
    
           public void run() {
             if (done)
               return;
             ticks++;
             delay += 1 / (20 * seconds);
             if (ticks > delay * 10) {
               ticks = 0;
    
               for (int itemstacks = 0; itemstacks < inv.getSize(); itemstacks++)
                 inv.setItem(itemstacks,
                     contents[(itemstacks + itemIndex)
                         % contents.length]);
               
               itemIndex++;
    
               if (delay >= 1) {
                 done = true;
                 new BukkitRunnable() {
    
                   @Override
                   public void run() {
                     reciever.getInventory().addItem(
                         contents[(4 + itemIndex - 1)
                             % contents.length]);
                     reciever.closeInventory();
                     cancel();
                   }
                 }.runTaskLater(plugin, 50);
                 cancel();
               }
             }
           }
         }.runTaskTimer(plugin, 0, 1);
       }
    }
    
    class InventoryClickHandler implements Listener {
       private String name = null;
    
       public InventoryClickHandler(Plugin plugin, String inventoryName) {
         Bukkit.getPluginManager().registerEvents(this, plugin);
         this.name = inventoryName;
       }
    
       @EventHandler
       public void onClick(InventoryClickEvent e) {
         if (e.getInventory().getTitle().equals(name)) {
           e.setCancelled(true);
         }
       }
    
       public void changeName(String name) {
         this.name = name;
       }
    }
    
    
    
    Usage
    Code:
           ItemStack[] items = new ItemStack[7];
           items[0] = new ItemStack(Material.BAKED_POTATO);
           items[1] = new ItemStack(Material.APPLE);
           items[2] = new ItemStack(Material.GOLD_AXE);
           items[3] = new ItemStack(Material.DIAMOND);
           items[4] = new ItemStack(Material.DIRT);
           items[5] = new ItemStack(Material.BOOKSHELF);
           items[6] = new ItemStack(Material.END_BRICKS);
    //This is all the rewards the player can get
       
           CrateInventory crate = new CrateInventory(9, items, "The name", this);
    //This is a crate. It has 9 slots, has the "items" as the rewards, and will have the title "The Name".
    //The "this" represents the main class's instance. If you are creating this crate in another class besides the main one,
    //you will need to replace "this" with an instance of the main class.
           crate.spin(8, player);
    
     
    Last edited: Sep 17, 2017
    DoggyCode™ likes this.
  2. Offline

    ThatsHawt

    If I use this in my plugin, do I need to give credit? (i will, don't worry)
     
  3. Offline

    I Al Istannen

    @ThatsHawt
    No licence means All rights reserved. Though I doubt this is the case with this code. Would be nice if you would list what you would like, @Zombie_Striker

    I can't speak for him, but I would assume just stating in the File header that the code is taken from Zombie_Striker would be enough. But, again I can't tell. You will need to wait for a response from him. And don't forget to tahg if you want your post to be seen.
     
  4. Offline

    Zombie_Striker

    @ThatsHawt
    I have just updated the code so it has the link to this util. Just keep the comment in your class if you copy and paste this util.
     
  5. Offline

    BeastyBoo

  6. Offline

    RenditionsRule

    @Zombie_Striker
    Would any modification of the class be allowed? And if so, would it be necessary if you needed to have other items in the inventory?
     
  7. Offline

    Zombie_Striker

    @RenditionsRule
    1. Yes, you can modify it. Even if I say you can't, I have no way of enforcing it.
    2. Other items can be added into the Itemstack[]. "Contents" represents all the items that will be in the inventory.
     
  8. Offline

    RenditionsRule

    @Zombie_Striker

    1. As a person with respect for such things, I wouldn't edit the class if I wasn't given permission.

    2. I mean items that it will not loop through. Sorry if this wasn't clear?
     
  9. Offline

    Zombie_Striker

    @RenditionsRule
    If you do not want certain items to be in the loop, just remove them. Either create a new crate without those items or use getContents[] to get all the contents in the crate, remove the ones you don't want, and use setContents to update the crate.
     
  10. Offline

    RenditionsRule

  11. Offline

    ShaneCraftDev

    @RenditionsRule These changes are impractible. For every new CrateInventory instance you make, you are registering a new eventlistener. This event is raised for each instance of CrateInventory registered to the same plugin.

    Code:java
    1.  
    2. @EventHandler
    3. public void onClick(InventoryClickEvent e) {
    4. if (e.getInventory().getName().equalsIgnoreCase(invname)) {
    5. e.setCancelled(true);
    6. }
    7. }


    If I name my chests the same as a registered CrateInventory object, I would not be able to use this chest (or any other tile entity with a container) until the the listener is unregistered. I haven't messed too much with inventories, but your local variable to test for inventory name is never set in an inventory instance. It would be best to alter the utility class to implement or extend (dont know whether there is an interface for inventories or not) to something that generic inventories use and open a new inventory of this instance. This way you're able to check if the inventory, from your canceling event, is an instance of the CrateInventory class.

    I do think the addition of events to this utility class is a good idea, but I would suggest you to register it per plugin and not per new instance of CrateInventory. Maybe create a private inner class that handles this per plugin.
     
  12. Offline

    RenditionsRule

    @ShaneCraftDev
    I had to move the Listener around a bit to help fix an issue I was having. As far as I'm aware, this works fine. I needed the event to be handled in the same class that contained the inventory name, since registering the listener in my main class would create a new instance of CrateInventory that cannot access that field.

    Every new instance of CrateInventory has its own fields with their own values. I set it up so that if you have more than 1 instance, it still cancels the event based on the name of the inventory.
     
  13. Offline

    Zombie_Striker

    @ShaneCraftDev
    Good idea. Adding it now.

    @RenditionsRule
    To add boarders, change the for loops from
    To
    Notice how the I is increased by one, and the maxsize is decreased by 1? This means that the slots at the end will not be accounted for, meaning you can add items at those edges without it being cycled.
     
  14. Offline

    DoggyCode™

    It's not random.. it stops at the same x or y point all the time. Basically just stopping on 2 items.
     
  15. Offline

    I Al Istannen

    @DoggyCode™
    Well, if you randomize the time it should be. It seems to not be made for randomizing, but for spinning items.

    What exactly is your goal though? Having some items, letting them spin and then stop at a random one?
     
  16. Offline

    DoggyCode™

    Alright, so I have an array of items in a GUI looking kinda like this:

    - [x] [y] [x] [z] [x] [y] [x] -

    x, y, and z all representing different kind of blocks. For the seconds parameter for the spin method I do int i = new Random().nextInt(20-10) + 10; These will generate a random number between 10 and 20. Then when it's done spinning, the winner (x, y, or z) would be the middle item. I use ItemStack winnerItem = Inventory#getItem(); However, it always lands on the two same items in the array all the time (either index 5 or index 3), never landed on any other index.

    EDIT:
    Here's a GIF of it in action: https://gyazo.com/2fefb953c560d919e637882ae5a2990e

    [​IMG]

    It always stops on that red one, one next to green, or the black before the red on the other side of the green
     
  17. Offline

    I Al Istannen

    @DoggyCode™
    I will make my own small random util, I have no idea what Zombie did there and not the patience to fully understand it.

    He does some calculations without any hint to why.

    I will start with it in ~20 mins.
     
  18. Offline

    Zombie_Striker

    @DoggyCode™
    I have just changed the code. Now it includes a randomizer. Have no idea why I forgot to add it.

    @I Al Istannen
    Editing the code. Hopefully it will be more straight forward after I edit it.
     
    cococow123 and DoggyCode™ like this.
  19. Offline

    I Al Istannen

    @Zombie_Striker
    Thanks ;) Things like "delay += (10 / (4 * Seconds))" are not always worth the time spend on understanding them.

    "int startingIndex = ThreadLocalRandom.current().nextInt(this.contents.length);"
    This idea is nice ;)

    @DoggyCode™
    Scratch my last comment, Zombie delivered ;)
     
    cococow123 and DoggyCode™ like this.
  20. Offline

    DoggyCode™

    As always :p

    EDIT: Defiantly noticing the difference, awesome util!
     
    Last edited: Jan 7, 2017
  21. Offline

    Zombie_Striker

    @DoggyCode™ @I Al Istannen
    Since the last edit, I have made a lot of changes to the code. Here are some of the important things to note:
    1. Opening the player's inventory has been moved into the spin method. Because of this, the open inventory line for the usage has been removed.
    2. For the "delay+=" line, the 10 represented the "slow-down" modifier, the 4 represented the amount of times the task ran every second, and the seconds is the amount of seconds it was meant to do. Originaly, the task looped only every 1/4 a second (5 ticks), so to me the 5*4=20 made sense. Since then, I have modified it so "4" became "20" and the "5" became a 1. It should now be clearer why I chose those numbers/ that algorthing.
    3. Also discussing the delay, I have moved it out of the tick-checker line. The delay will now slowdown continuously instead of only when the items changed slots. This means it should now slow down smoothly.
    4. I have changed the modifier so that it stops at a slower rate. Now, there should not be a sudden stop when it chooses an item.
    5. The selected item will now stay in the inventory for 2.5 seconds, giving the player enough time to see which item was chosen.
    6. The inventory will now close when the item has been added. The player no longer has to press e *(or their selected close button) to close the inventory.
    7. You can now choose different names for the crate without affecting any existing crates running at that time. Now, each player can have their name in the title with affecting another player's crate.
    8. A lot of refractoring. All class names and variable names should now be descriptive.
    If you want to upgrade from my previous code to the newest one (something that I would recommend), copy the new code to the new class, remove the "setDefaultName" line and add a name when you create the crate instance (so it would be "new CrateInventory(slots,items,Name,this)" )
     
  22. Offline

    DoggyCode™

    Honestly just what I was looking for. However, I'm not creating a player specific inventory. I'm creating a sort of global inventory which every player can see, and will update at the same time. Will this work, as I can't supply a Player object.


    Sent from my iPhone using Tapatalk
     
    cococow123 likes this.
  23. Offline

    Zombie_Striker

    @DoggyCode™
    Yes. You can still open the same inventory for all the players, so all the players can see the same inventory. Just remember that only one player will receive the item. Just in case you want to give all the players an item, get the inventory and loop through all the "viewers".
     
  24. @Zombie_Striker
    Just out of curiosity, is there a reason you're using io.netty.util.internal.ThreadLocalRandom instead of
    java.util.concurrent.ThreadLocalRandom?
     
  25. Offline

    DoggyCode™

    Your "spin" method requires a Player in its parameters, should i change that to Inventory, and just directly change that?

    EDIT: I have it working now, but what's this line for?
    /*reciever .addItem( contents[(4 + itemId-1) % contents.length]);*/

    I commented it out, and all it does is to duplicate one of the items in the contents array (instead of it becoming 1 amount of the ItemStack, it becomes 2, removing it fixed it.
     
    Last edited: Jan 8, 2017
  26. Offline

    Zombie_Striker

    @AlvinB
    Nope. There is no reason. I was not paying attention to it when I was using the auto-import. Fixed.

    @DoggyCode™
    Receiver is the player that will receive the item. Changing it to an inventory will put the item into the inventory, meaning it will be duplicated and the receiver will not get the item. Turning it back to a player will stop it from being duplicated and actually give the player the item.
     
    cococow123 and DoggyCode™ like this.
  27. Offline

    Xomka2280

    @Zombie_Striker
    And how to make random of things, that they went not in a certain order, and in casual?
     
    Last edited: Nov 5, 2017
  28. Offline

    Zombie_Striker

    @Xomka2280
    The system is not currently set up to add random items. In order to make it add random items, you would either need to A) add a long series of items so it seems semi-random, or B) make some changes to it add a new random item at the right slot, moves all the slots to the left instead of using indexes, and give the player the item in the center instead of using indexes.

    Also, i'm not sure what you mean by "casual".
     
Thread Status:
Not open for further replies.

Share This Page