Thread: An extremely fast and efficient NPC drop table system

Page 1 of 2 12 LastLast
Results 1 to 10 of 16
  1. #1 An extremely fast and efficient NPC drop table system 
    Banned Market Banned Market Banned


    Join Date
    Jan 2011
    Age
    23
    Posts
    3,115
    Thanks given
    1,198
    Thanks received
    1,479
    Rep Power
    0
    Highly inspired by [Only registered and activated users can see links. ] so thanks to Poolside Queer



    Why you should use this over other drop systems

    • Its more than likely faster and more effcient
    • Uses a decimal for accuracy but only rounds to 3 decimal places to prevent results that are too insignifcant
    • Gives items in random amounts (if the amount entered is greator than 1)
    • Has dynamic support for things like ring of wealth
    • Has support for debugging rare drops
    • Clean and documented so you can understand everything



    How the system works

    There are an array of npcs who use the specified drop table along with a dynamic drop table and a rare drop table. Every item in the dynamic drop table will be compared against the pseudo-randomly generated number to determine if it will be dropped. This means that in theory every single item in the dynamic table can be dropped that kill.

    Code:
                for (NpcDrop drop : dynamic) {
                    if (drop == null) {
                        continue;
                    }
    
                    /** Round to 3 decimal places for decreased accuracy. */
                    double rollRound = Math.round(roll.nextDouble() * 1000.0) / 1000.0;
    
                    /** Compare the roll against the bet. */
                    if (rollRound <= drop.getBet()) {
    
                        /** Roll was successful! Add the item to the drops. */
                        item[slot++] = drop.toItem();
                    }
                }

    The same thing does not apply for the rare table, because only one drop is selected randomly out of that table regardless of the amount of drops.

    Code:
     /** The bet modification value. */
                BetModification betMod = getBetModification(player);
    
                /** Select one random item from out of the rare table. */
                NpcDrop rareDrop = Misc.randomElement(rare);
    
                /** Round to 3 decimal places for decreased accuracy. */
                double rollRound = Math.round(roll.nextDouble() * 1000.0) / 1000.0;
    
                /**
                 * Compare the roll against the bet, including the bet
                 * modifications.
                 */
                if (rollRound <= (rareDrop.getBet() + betMod.getMod())) {
    
                    /** Roll was successful! Add the item to the drops. */
                    item[slot++] = rareDrop.toItem();
    
                    /** Drop successful! Apply any bet modification effects here. */
                    betMod.itemPassed(player, rareDrop);
                } else {
    
                    /** Drop unsuccessful! Apply any bet modification effects here. */
                    betMod.itemFailed(player, rareDrop);
                }

    There are support for things such as the ring of wealth, although they are referred to as BetModfications. The bet modifications are only applied to the rare drop table and because of the design can be used to easily fire miscellaneous actions when a rare item is successfully dropped (or even if it is unsuccessful!).

    Code:
                if (rollRound <= (rareDrop.getBet() + betMod.getMod())) {
    
                    /** Roll was successful! Add the item to the drops. */
                    item[slot++] = rareDrop.toItem();
    
                    /** Drop successful! Apply any bet modification effects here. */
                    betMod.itemPassed(player, rareDrop);
                } else {
    
                    /** Drop unsuccessful! Apply any bet modification effects here. */
                    betMod.itemFailed(player, rareDrop);
                }

    Adding the drop system

    Not going to bother spoonfeeding on how to add it, but I'll provide all of the code essential to getting it working.

    The main drop table class (has about 3 nested inner classes)
    Code:
    package server.world.entity.npc;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    
    import server.util.Misc;
    import server.world.entity.player.Player;
    import server.world.item.Item;
    
    /**
     * The table that will be used to hold every drop possible for an {@link Npc}
     * along with various functions to calculate which items will be dropped.
     * 
     * @author lare96
     */
    public class NpcDropTable {
    
        /**
         * Will be used to hold all of the drops for each npc. We use a map for easy
         * and fast retrieval of the npc's table.
         */
        private static Map<Integer, NpcDropTable> drops = new HashMap<Integer, NpcDropTable>();
    
        /**
         * Will be used to generate a random number called a <code>roll</code> that
         * will be compared against the number assigned to the drop called a
         * <code>bet</code> that will determine if the item can be dropped or not.
         */
        private static Random roll = new Random();
    
        /** The npc(s) who uses this drop table. */
        private int[] npcs;
    
        /** The dynamic drop table assigned to this npc. */
        private NpcDrop[] dynamic;
    
        /** The rare table assigned to this npc. */
        private NpcDrop[] rare;
    
        /**
         * Create a new {@link NpcDropTable}.
         * 
         * @param npc
         *        the npc(s) who use this drop table.
         * @param dynamic
         *        the dynamic drop table assigned to this npc.
         * @param rare
         *        the rare table assigned to this npc.
         */
        public NpcDropTable(int[] npcs, NpcDrop[] dynamic, NpcDrop[] rare) {
            this.npcs = npcs;
            this.dynamic = dynamic;
            this.rare = rare;
        }
    
        /**
         * Calculates a set of drops with the intention that they will be dropped on
         * death, although they can be used however one would like. Please note that
         * this is not a static implementation, meaning that calling this method
         * will return a different set of items each time.
         * 
         * @param player
         *        the player these items are being calculated for.
         * @return the calculated items that will be dropped.
         */
        public Item[] calculateDrops(Player player) {
    
            /** The placeholder that will be used to track our slot position. */
            int slot = 0;
    
            /** The array of items that will be dropped. */
            Item[] item = new Item[getDropLength()];
    
            /** Gamble all items in the dynamic table. */
            if (dynamic != null
                    && dynamic.length > 0) {
                for (NpcDrop drop : dynamic) {
                    if (drop == null) {
                        continue;
                    }
    
                    /** Round to 3 decimal places for decreased accuracy. */
                    double rollRound = Math.round(roll.nextDouble() * 1000.0) / 1000.0;
    
                    /** Compare the roll against the bet. */
                    if (rollRound <= drop.getBet()) {
    
                        /** Roll was successful! Add the item to the drops. */
                        item[slot++] = drop.toItem();
                    }
                }
            }
    
            /** Gamble one item in the rare table. */
            if (rare != null
                    && rare.length > 0) {
    
                /** The bet modification value. */
                BetModification betMod = getBetModification(player);
    
                /** Select one random item from out of the rare table. */
                NpcDrop rareDrop = Misc.randomElement(rare);
    
                /** Round to 3 decimal places for decreased accuracy. */
                double rollRound = Math.round(roll.nextDouble() * 1000.0) / 1000.0;
    
                /**
                 * Compare the roll against the bet, including the bet
                 * modifications.
                 */
                if (rollRound <= (rareDrop.getBet() + betMod.getMod())) {
    
                    /** Roll was successful! Add the item to the drops. */
                    item[slot++] = rareDrop.toItem();
    
                    /** Drop successful! Apply any bet modification effects here. */
                    betMod.itemPassed(player, rareDrop);
                } else {
    
                    /** Drop unsuccessful! Apply any bet modification effects here. */
                    betMod.itemFailed(player, rareDrop);
                }
    
            }
    
            return item;
        }
    
        /**
         * Calculates a set of rare drops (without bet modifications) and prints out
         * if any of them were successful out of 1 million kills. If they were
         * successful it prints how many kills it took to make that drop successful.
         * If none at all were successful out of a million kills it will print an
         * indication of that. Please note that this method will exit the
         * application once a rare drop is made or once the calculations have been
         * completed with no successful drops. This method is for debugging only and
         * <b>should not</b> be used during runtime.
         */
        public void calculateDropsDebug() {
    
            /** Gamble one item in the rare table. */
            if (rare != null
                    && rare.length > 0) {
    
                for (int i = 0; i < 1000000; i++) {
    
                    /** Select one random item from out of the rare table. */
                    NpcDrop rareDrop = Misc.randomElement(rare);
    
                    /** Round to 3 decimal places for decreased accuracy. */
                    double rollRound = Math.round(roll.nextDouble() * 1000.0) / 1000.0;
    
                    /** Compare the roll against the bet. */
                    if (rollRound <= rareDrop.getBet()) {
                        System.out.println("RARE ITEM DROPPED[item = "
                                + rareDrop.toItem().getDefinition().getItemName()
                                + ", kills= "
                                + (i + 1)
                                + "]");
                        System.exit(0);
                    }
                }
    
                System.out.println("NO RARE ITEMS DROPPED");
                System.exit(0);
            }
        }
    
        /**
         * Gets the max possible size of the array that will hold the drops. Any
         * <code>null</code> elements will be filtered out when the items are
         * dropped.
         * 
         * @return the size of the array that will hold the drops.
         */
        private int getDropLength() {
    
            /** The common table length plus one, for one rare item. */
            return dynamic.length + 1;
        }
    
        /**
         * Any modifications to the <code>bet</code> should be put here. The number
         * returned will increase the chance of a rare item being dropped.
         * Alternatively, you can return a negative {@link Double} to decrease the
         * chance of a rare item being dropped.
         * 
         * @param player
         *        the player these items are being dropped for.
         * @return the modification to the <code>bet</code>.
         */
        private static BetModification getBetModification(Player player) {
    
            /** Check if we have a ring of wealth equipped. */
            if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_RING) == 2572) {
    
                /** Chance to drop a rare item is increased by 0.025, which is 2.5%. */
                return new BetModification(0.025) {
                    @Override
                    public void itemPassed(Player player, NpcDrop item) {
    
                        /** Item dropped, do ring of wealth stuff. */
                        if (roll.nextBoolean()) {
                            player.getEquipment().removeItem(Misc.EQUIPMENT_SLOT_RING);
                            player.getPacketBuilder().sendMessage("Your ring of wealth takes effect and crumbles into dust!");
                        } else {
                            player.getPacketBuilder().sendMessage("Your ring of wealth takes effect and keeps itself intact!");
                        }
                    }
    
                    @Override
                    public void itemFailed(Player player, NpcDrop item) {
    
                        /** Item did not drop, nothing happens to our ring. */
                    }
                };
            }
            return BetModification.DEFAULT_BET_MOD;
        }
    
        /**
         * Gets all of the drops for each npc.
         * 
         * @return all of the drops for each npc.
         */
        public static Map<Integer, NpcDropTable> getAllDrops() {
            return drops;
        }
    
        /**
         * Gets the npc(s) who use this drop table.
         * 
         * @return the npc(s) who use this drop table.
         */
        public int[] getNpcs() {
            return npcs;
        }
    
        /**
         * A container class used to hold the bet modification.
         * 
         * @author lare96
         */
        public static abstract class BetModification {
    
            /**
             * The default bet modification. We use this so we do not have to check
             * for <code>null</code> values.
             */
            public static final BetModification DEFAULT_BET_MOD = new BetModification(0.0) {
                @Override
                public void itemPassed(Player player, NpcDrop item) {
    
                    /** Nothing happens, no bet modification. */
                }
    
                @Override
                public void itemFailed(Player player, NpcDrop item) {
    
                    /** Nothing happens, no bet modification. */
                }
            };
    
            /** The fixed bet modification. */
            private double mod;
    
            /**
             * Create a new {@link BetModification}.
             * 
             * @param mod
             *        the fixed bet modification.
             */
            public BetModification(double mod) {
                this.mod = mod;
            }
    
            /**
             * What happens when the item drops.
             * 
             * @param player
             *        the player who is receiving the drops.
             * @param item
             *        the item being dropped.
             */
            public abstract void itemPassed(Player player, NpcDrop item);
    
            /**
             * What happens when the item was selected, but not successful enough to
             * be dropped.
             * 
             * @param player
             *        the player who is receiving the drops.
             * @param item
             *        the item being dropped.
             */
            public abstract void itemFailed(Player player, NpcDrop item);
    
            /**
             * Gets the fixed bet modification.
             * 
             * @return the fixed bet modification.
             */
            public double getMod() {
                return mod;
            }
        }
    
        /**
         * A single item that will be gambled when the assigned npc dies in order to
         * determine if it will be dropped or not.
         * 
         * @author lare96
         */
        public static class NpcDrop {
    
            /** The id of item that will be dropped. */
            private int id;
    
            /** The minimum amount of item that will be dropped. */
            private int minimum;
    
            /** The maximum amount of item that will be dropped. */
            private int maximum;
    
            /**
             * The chance that this item will be dropped. The lowest chance being
             * 0.0 (0%, impossible) and the highest being 1.0 (100%, always).
             */
            private double bet;
    
            /**
             * Create a new {@link DeathDrop}.
             * 
             * @param id
             *        the id of item that will be dropped.
             * @param minimum
             *        the minimum amount of item that will be dropped.
             * @param maximum
             *        the maximum amount of item that will be dropped.
             * @param bet
             *        the chance that this item will be dropped.
             */
            public NpcDrop(int id, int minimum, int maximum, double bet) {
                this.id = id;
                this.minimum = minimum;
                this.maximum = maximum;
                this.bet = bet;
            }
    
            /**
             * Turns the id and amount into an {@link Item} instance.
             * 
             * @return the item instance representing the id and amount of this
             *         drop.
             */
            public Item toItem() {
                return new Item(id, Misc.inclusiveRandom(minimum, maximum));
            }
    
            /**
             * Gets the chance that this item will be dropped.
             * 
             * @return the chance that this item will be dropped.
             */
            public double getBet() {
                return bet;
            }
        }
    }

    Loading the drops from the JSON file

    Code:
        /**
         * Parse the all of the data for npc drops.
         * 
         * @throws Exception
         *         if any errors occur while parsing this file.
         */
        public static void loadNpcDrops() throws Exception {
            JsonParser parser = new JsonParser();
            JsonArray array = (JsonArray) parser.parse(new FileReader(new File("./data/json/npcs/world_npc_drops.json")));
            final Gson builder = new GsonBuilder().create();
    
            for (int i = 0; i < array.size(); i++) {
                JsonObject reader = (JsonObject) array.get(i);
    
                final int id[] = builder.fromJson(reader.get("id"), int[].class);
                final NpcDrop[] dynamic = builder.fromJson(reader.get("dynamic"), NpcDrop[].class);
                final NpcDrop[] rare = builder.fromJson(reader.get("rare"), NpcDrop[].class);
    
                for (int e : id) {
                    NpcDropTable.getAllDrops().put(e, new NpcDropTable(id, dynamic, rare));
                }
            }
        }

    An example of how it would it in the JSON file for an abyssal demon

    Code:
    {
            "id": [
             	1615
             ],
            "dynamic": [
                {
                   "id": 592,
                   "minimum": 1,
                   "maximum": 1,
                   "bet": 1.0
                },
                {
                   "id": 995,
                   "minimum": 1000,
                   "maximum": 100000,
                   "bet": 0.15
                }
             ],
            "rare": [
                {
                   "id": 4151,
                   "minimum": 1,
                   "maximum": 1,
                   "bet": 0.001
                }
    	]	
    }


    Debugging Results

    I used the debugging utility that comes with this to see how often a whip would be dropped for an abyssal demon, and these were the results

    Code:
    RARE ITEM DROPPED[item = Abyssal whip, kills= 211]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 163]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 461]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 7]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 304]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 564]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 45]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 377]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 435]
    RARE ITEM DROPPED[item = Abyssal whip, kills= 53]
    Based on these results a whip would drop at around every 257.2 kills
    Reply With Quote  
     

  2. Thankful user:


  3. #2  
    Super Donator

    Janizary's Avatar
    Join Date
    Jul 2009
    Age
    19
    Posts
    2,132
    Thanks given
    109
    Thanks received
    137
    Rep Power
    1031
    Looks really clean. Thanks.

    [Only registered and activated users can see links. ]
    Quote Originally Posted by Pirlo View Post
    I don't think the cold war existed during/before WWI
    Reply With Quote  
     

  4. #3  
    Registered Member
    Zivik's Avatar
    Join Date
    Oct 2007
    Age
    25
    Posts
    4,432
    Thanks given
    892
    Thanks received
    1,525
    Rep Power
    3228
    Neat. Good work on this Lare.
    [Only registered and activated users can see links. ]
    Reply With Quote  
     

  5. #4  
    Registered Member

    Join Date
    Jan 2013
    Posts
    586
    Thanks given
    343
    Thanks received
    146
    Rep Power
    163
    Fantastic work, love it.

    I don't know what you heard about me
    But a pleb can't get a rep out of me
    No rep, no thanks, you can't see
    That I'm a mother****ing P - I - M - P

    Cool Cats
    Thakiller 🎺 Scu11 🎸 Tyler 🎷 Lare 🎤 ur nan
    Reply With Quote  
     

  6. #5  
    Stand guard at the door of your mind

    Proto's Avatar
    Join Date
    Jul 2011
    Age
    26
    Posts
    1,234
    Thanks given
    152
    Thanks received
    413
    Rep Power
    462
    Well done, great documenting/explanation on your code
    [Only registered and activated users can see links. ]



    [Only registered and activated users can see links. ]
    Reply With Quote  
     

  7. #6  
    Registered Member
    Join Date
    Jan 2014
    Posts
    818
    Thanks given
    69
    Thanks received
    47
    Rep Power
    9
    Very clean. Might use for my drop system
    Reply With Quote  
     

  8. #7  
    need java lessons
    Eclipse's Avatar
    Join Date
    Aug 2012
    Posts
    4,472
    Thanks given
    686
    Thanks received
    898
    Rep Power
    490
    sick bb

    Quote Originally Posted by jerryrocks317 View Post
    i am 14 and have my own laptop im on almost 24/7 currently creating rsps lol so please get off my thread lol
    Reply With Quote  
     

  9. #8  
    Banned Market Banned Market Banned


    Join Date
    Jan 2011
    Age
    23
    Posts
    3,115
    Thanks given
    1,198
    Thanks received
    1,479
    Rep Power
    0
    thanks!
    Reply With Quote  
     

  10. #9  

    Supreme's Avatar
    Join Date
    May 2011
    Posts
    776
    Thanks given
    460
    Thanks received
    278
    Rep Power
    3494
    Will definitley implant this, thank you very much!
    Reply With Quote  
     

  11. #10  
    Banned Market Banned Market Banned


    Join Date
    Jan 2011
    Age
    23
    Posts
    3,115
    Thanks given
    1,198
    Thanks received
    1,479
    Rep Power
    0
    implant lol
    Reply With Quote  
     

  12. Thankful users:


Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 56
    Last Post: 07-25-2016, 08:53 AM
  2. Rare Drop Table System
    By South-Park in forum Snippets
    Replies: 4
    Last Post: 08-20-2013, 02:51 PM
  3. Replies: 366
    Last Post: 08-30-2011, 10:21 PM
  4. [pi] npc drops and more (rep+)
    By Lord Stark in forum Help
    Replies: 3
    Last Post: 11-07-2010, 06:28 PM
  5. npcspwans, npclist and npc drops.
    By _slam in forum Configuration
    Replies: 5
    Last Post: 02-12-2010, 07:59 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •