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