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