001    package net.minecraftforge.common;
002    
003    import java.util.*;
004    
005    import net.minecraft.item.Item;
006    import net.minecraft.item.ItemStack;
007    import net.minecraft.util.WeightedRandom;
008    import net.minecraft.util.WeightedRandomChestContent;
009    import net.minecraft.world.WorldServer;
010    import net.minecraft.world.gen.structure.*;
011    
012    public class ChestGenHooks
013    {
014        //Currently implemented categories for chests/dispensers, Dungeon loot is still in DungeonHooks
015        public static final String MINESHAFT_CORRIDOR       = "mineshaftCorridor";
016        public static final String PYRAMID_DESERT_CHEST     = "pyramidDesertyChest";
017        public static final String PYRAMID_JUNGLE_CHEST     = "pyramidJungleChest";
018        public static final String PYRAMID_JUNGLE_DISPENSER = "pyramidJungleDispenser";
019        public static final String STRONGHOLD_CORRIDOR      = "strongholdCorridor";
020        public static final String STRONGHOLD_LIBRARY       = "strongholdLibrary";
021        public static final String STRONGHOLD_CROSSING      = "strongholdCrossing";
022        public static final String VILLAGE_BLACKSMITH       = "villageBlacksmith";
023        public static final String BONUS_CHEST              = "bonusChest";
024        public static final String DUNGEON_CHEST            = "dungeonChest";
025    
026        private static final HashMap<String, ChestGenHooks> chestInfo = new HashMap<String, ChestGenHooks>();
027        private static boolean hasInit = false;
028        static 
029        {
030            init();
031        }
032    
033        private static void init()
034        {
035            if (hasInit)
036            {
037                return;
038            }
039    
040            hasInit = true;
041    
042            addInfo(MINESHAFT_CORRIDOR,       StructureMineshaftPieces.mineshaftChestContents,                         3,  7);
043            addInfo(PYRAMID_DESERT_CHEST,     ComponentScatteredFeatureDesertPyramid.itemsToGenerateInTemple,          2,  7);
044            addInfo(PYRAMID_JUNGLE_CHEST,     ComponentScatteredFeatureJunglePyramid.junglePyramidsChestContents,      2,  7);
045            addInfo(PYRAMID_JUNGLE_DISPENSER, ComponentScatteredFeatureJunglePyramid.junglePyramidsDispenserContents,  2,  2);
046            addInfo(STRONGHOLD_CORRIDOR,      ComponentStrongholdChestCorridor.strongholdChestContents,                2,  4);
047            addInfo(STRONGHOLD_LIBRARY,       ComponentStrongholdLibrary.strongholdLibraryChestContents,               1,  5);
048            addInfo(STRONGHOLD_CROSSING,      ComponentStrongholdRoomCrossing.strongholdRoomCrossingChestContents,     1,  5);
049            addInfo(VILLAGE_BLACKSMITH,       ComponentVillageHouse2.villageBlacksmithChestContents,                   3,  9);
050            addInfo(BONUS_CHEST,              WorldServer.bonusChestContent,                                          10, 10);
051    
052            ItemStack book = new ItemStack(Item.field_92053_bW, 1, 0);
053            WeightedRandomChestContent tmp = new WeightedRandomChestContent(book, 1, 1, 1);
054            getInfo(MINESHAFT_CORRIDOR  ).addItem(tmp);
055            getInfo(PYRAMID_DESERT_CHEST).addItem(tmp);        
056            getInfo(PYRAMID_JUNGLE_CHEST).addItem(tmp);
057            getInfo(STRONGHOLD_CORRIDOR ).addItem(tmp);
058            getInfo(STRONGHOLD_LIBRARY  ).addItem(new WeightedRandomChestContent(book, 1, 5, 2));
059            getInfo(STRONGHOLD_CROSSING ).addItem(tmp);
060    
061            //Wish Dungeons would get on the same wave length as other world gen...
062            ChestGenHooks d = new ChestGenHooks(DUNGEON_CHEST);
063            d.countMin = 8;
064            d.countMax = 8;
065            chestInfo.put(DUNGEON_CHEST, d);
066            addDungeonLoot(d, new ItemStack(Item.saddle),          100, 1, 1);
067            addDungeonLoot(d, new ItemStack(Item.ingotIron),       100, 1, 4);
068            addDungeonLoot(d, new ItemStack(Item.bread),           100, 1, 1);
069            addDungeonLoot(d, new ItemStack(Item.wheat),           100, 1, 4);
070            addDungeonLoot(d, new ItemStack(Item.gunpowder),       100, 1, 4);
071            addDungeonLoot(d, new ItemStack(Item.silk),            100, 1, 4);
072            addDungeonLoot(d, new ItemStack(Item.bucketEmpty),     100, 1, 1);
073            addDungeonLoot(d, new ItemStack(Item.appleGold),         1, 1, 1);
074            addDungeonLoot(d, new ItemStack(Item.redstone),         50, 1, 4);
075            addDungeonLoot(d, new ItemStack(Item.record13),          5, 1, 1);
076            addDungeonLoot(d, new ItemStack(Item.recordCat),         5, 1, 1);
077            addDungeonLoot(d, new ItemStack(Item.dyePowder, 1, 3), 100, 1, 1);
078            addDungeonLoot(d, book,                                100, 1, 1);
079        }
080        
081        static void addDungeonLoot(ChestGenHooks dungeon, ItemStack item, int weight, int min, int max)
082        {
083            dungeon.addItem(new WeightedRandomChestContent(item, min, max, weight));
084        }
085    
086        private static void addInfo(String category, WeightedRandomChestContent[] items, int min, int max)
087        {
088            chestInfo.put(category, new ChestGenHooks(category, items, min, max));
089        }
090    
091        /**
092         * Retrieves, or creates the info class for the specified category.
093         * 
094         * @param category The category name
095         * @return A instance of ChestGenHooks for the specified category.
096         */
097        public static ChestGenHooks getInfo(String category)
098        {
099            if (!chestInfo.containsKey(category))
100            {
101                chestInfo.put(category, new ChestGenHooks(category));
102            }
103            return chestInfo.get(category);
104        }
105    
106        /**
107         * Generates an array of items based on the input min/max count.
108         * If the stack can not hold the total amount, it will be split into 
109         * stacks of size 1.
110         * 
111         * @param rand A random number generator
112         * @param source Source item stack
113         * @param min Minimum number of items
114         * @param max Maximum number of items
115         * @return An array containing the generated item stacks 
116         */
117        public static ItemStack[] generateStacks(Random rand, ItemStack source, int min, int max)
118        {
119            int count = min + (rand.nextInt(max - min + 1));
120    
121            ItemStack[] ret;
122            if (source.getItem() == null)
123            {
124                ret = new ItemStack[0];
125            }
126            else if (count > source.getItem().getItemStackLimit())
127            {
128                ret = new ItemStack[count];
129                for (int x = 0; x < count; x++)
130                {
131                    ret[x] = source.copy();
132                    ret[x].stackSize = 1;
133                }
134            }
135            else
136            {
137                ret = new ItemStack[1];
138                ret[0] = source.copy();
139                ret[0].stackSize = count;
140            }
141            return ret;
142        }
143    
144        //shortcut functions, See the non-static versions below
145        public static WeightedRandomChestContent[] getItems(String category, Random rnd){ return getInfo(category).getItems(rnd); }
146        public static int getCount(String category, Random rand){ return getInfo(category).getCount(rand); }
147        public static void addItem(String category, WeightedRandomChestContent item){ getInfo(category).addItem(item); }
148        public static void removeItem(String category, ItemStack item){ getInfo(category).removeItem(item); }
149        public static ItemStack getOneItem(String category, Random rand){ return getInfo(category).getOneItem(rand); }
150    
151        private String category;
152        private int countMin = 0;
153        private int countMax = 0;
154        //TO-DO: Privatize this once again when we remove the Deprecated stuff in DungeonHooks
155        ArrayList<WeightedRandomChestContent> contents = new ArrayList<WeightedRandomChestContent>();
156    
157        public ChestGenHooks(String category)
158        {
159            this.category = category;
160        }
161        
162        public ChestGenHooks(String category, WeightedRandomChestContent[] items, int min, int max)
163        {
164            this(category);
165            for (WeightedRandomChestContent item : items)
166            {
167                contents.add(item);
168            }
169            countMin = min;
170            countMax = max;
171        }
172        
173        /**
174         * Adds a new entry into the possible items to generate.
175         * 
176         * @param item The item to add.
177         */
178        public void addItem(WeightedRandomChestContent item)
179        {
180            contents.add(item);
181        }
182        
183        /**
184         * Removes all items that match the input item stack, Only metadata and item ID are checked.
185         * If the input item has a metadata of -1, all metadatas will match.
186         * 
187         * @param item The item to check
188         */
189        public void removeItem(ItemStack item)
190        {
191            Iterator<WeightedRandomChestContent> itr = contents.iterator();
192            while(itr.hasNext())
193            {
194                WeightedRandomChestContent cont = itr.next();
195                if (item.isItemEqual(cont.theItemId) || (item.getItemDamage() == -1 && item.itemID == cont.theItemId.itemID))
196                {
197                    itr.remove();
198                }
199            }
200        }
201    
202        /**
203         * Gets an array of all random objects that are associated with this category.
204         * 
205         * @return The random objects
206         */
207        public WeightedRandomChestContent[] getItems(Random rnd)
208        {
209            ArrayList<WeightedRandomChestContent> ret = new ArrayList<WeightedRandomChestContent>();
210            
211            for (WeightedRandomChestContent orig : contents)
212            {
213                Item item = orig.theItemId.getItem();
214    
215                if (item != null)
216                {
217                    WeightedRandomChestContent n = item.getChestGenBase(this, rnd, orig);
218                    if (n != null)
219                    {
220                        ret.add(n);
221                    }
222                }
223            }
224    
225            return ret.toArray(new WeightedRandomChestContent[ret.size()]);
226        }
227    
228        /**
229         * Gets a random number between countMin and countMax.
230         * 
231         * @param rand A RNG
232         * @return A random number where countMin <= num <= countMax
233         */
234        public int getCount(Random rand)
235        {
236            return countMin < countMax ? countMin + rand.nextInt(countMax - countMin) : countMin;
237        }
238        
239        /**
240         * Returns a single ItemStack from the possible items in this registry,
241         * Useful if you just want a quick and dirty random Item.
242         * 
243         * @param rand  A Random Number gen
244         * @return A single ItemStack, or null if it could not get one.
245         */
246        public ItemStack getOneItem(Random rand)
247        {
248            WeightedRandomChestContent[] items = getItems(rand);
249            WeightedRandomChestContent item = (WeightedRandomChestContent)WeightedRandom.getRandomItem(rand, items);
250            ItemStack[] stacks = ChestGenHooks.generateStacks(rand, item.theItemId, item.theMinimumChanceToGenerateItem, item.theMaximumChanceToGenerateItem);
251            return (stacks.length > 0 ? stacks[0] : null);
252        }
253    
254        //Accessors
255        public int getMin(){ return countMin; }
256        public int getMax(){ return countMax; }
257        public void setMin(int value){ countMin = value; }
258        public void setMax(int value){ countMax = value; }
259    }