    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}