001package net.minecraftforge.common;
002
003import java.util.*;
004import java.util.Map.Entry;
005
006import com.google.common.collect.ArrayListMultimap;
007import com.google.common.collect.ListMultimap;
008
009import cpw.mods.fml.common.FMLLog;
010import cpw.mods.fml.common.Loader;
011
012import net.minecraft.block.Block;
013import net.minecraft.entity.EntityLiving;
014import net.minecraft.entity.item.EntityItem;
015import net.minecraft.entity.player.EntityPlayer;
016import net.minecraft.item.Item;
017import net.minecraft.item.ItemArmor;
018import net.minecraft.item.ItemAxe;
019import net.minecraft.item.ItemPickaxe;
020import net.minecraft.item.ItemSpade;
021import net.minecraft.item.ItemStack;
022import net.minecraft.util.DamageSource;
023import net.minecraft.util.EnumMovingObjectType;
024import net.minecraft.util.MovingObjectPosition;
025import net.minecraft.util.WeightedRandom;
026import net.minecraft.util.WeightedRandomItem;
027import net.minecraft.world.World;
028import net.minecraftforge.event.ForgeEventFactory;
029import net.minecraftforge.event.entity.item.ItemTossEvent;
030import net.minecraftforge.event.entity.living.*;
031import net.minecraftforge.event.entity.living.LivingEvent.*;
032
033public class ForgeHooks
034{
035    static class GrassEntry extends WeightedRandomItem
036    {
037        public final Block block;
038        public final int metadata;
039        public GrassEntry(Block block, int meta, int weight)
040        {
041            super(weight);
042            this.block = block;
043            this.metadata = meta;
044        }
045    }
046
047    static class SeedEntry extends WeightedRandomItem
048    {
049        public final ItemStack seed;
050        public SeedEntry(ItemStack seed, int weight)
051        {
052            super(weight);
053            this.seed = seed;
054        }
055    }
056    static final List<GrassEntry> grassList = new ArrayList<GrassEntry>();
057    static final List<SeedEntry> seedList = new ArrayList<SeedEntry>();
058
059    public static void plantGrass(World world, int x, int y, int z)
060    {
061        GrassEntry grass = (GrassEntry)WeightedRandom.getRandomItem(world.rand, grassList);
062        if (grass == null || grass.block == null || !grass.block.canBlockStay(world, x, y, z))
063        {
064            return;
065        }
066        world.setBlockAndMetadataWithNotify(x, y, z, grass.block.blockID, grass.metadata);
067    }
068
069    public static ItemStack getGrassSeed(World world)
070    {
071        SeedEntry entry = (SeedEntry)WeightedRandom.getRandomItem(world.rand, seedList);
072        if (entry == null || entry.seed == null)
073        {
074            return null;
075        }
076        return entry.seed.copy();
077    }
078
079    private static boolean toolInit = false;
080    static HashMap<Item, List> toolClasses = new HashMap<Item, List>();
081    static HashMap<List, Integer> toolHarvestLevels = new HashMap<List, Integer>();
082    static HashSet<List> toolEffectiveness = new HashSet<List>();
083
084    public static boolean canHarvestBlock(Block block, EntityPlayer player, int metadata)
085    {
086        if (block.blockMaterial.isToolNotRequired())
087        {
088            return true;
089        }
090
091        ItemStack stack = player.inventory.getCurrentItem();
092        if (stack == null)
093        {
094            return player.canHarvestBlock(block);
095        }
096
097        List info = toolClasses.get(stack.getItem());
098        if (info == null)
099        {
100            return player.canHarvestBlock(block);
101        }
102
103        Object[] tmp = info.toArray();
104        String toolClass = (String)tmp[0];
105        int harvestLevel = (Integer)tmp[1];
106
107        Integer blockHarvestLevel = toolHarvestLevels.get(Arrays.asList(block, metadata, toolClass));
108        if (blockHarvestLevel == null)
109        {
110            return player.canHarvestBlock(block);
111        }
112
113        if (blockHarvestLevel > harvestLevel)
114        {
115            return false;
116        }
117        return true;
118    }
119    
120    public static boolean canToolHarvestBlock(Block block, int metadata, ItemStack stack)
121    {
122        if (stack == null) return false;
123        List info = toolClasses.get(stack.getItem());
124        if (info == null) return false;
125
126        Object[] tmp = info.toArray();
127        String toolClass = (String)tmp[0];
128        int harvestLevel = (Integer)tmp[1];
129
130        Integer blockHarvestLevel = toolHarvestLevels.get(Arrays.asList(block, metadata, toolClass));
131        return !(blockHarvestLevel == null || blockHarvestLevel > harvestLevel);
132    }
133
134    public static float blockStrength(Block block, EntityPlayer player, World world, int x, int y, int z)
135    {
136        int metadata = world.getBlockMetadata(x, y, z);
137        float hardness = block.getBlockHardness(world, x, y, z);
138        if (hardness < 0.0F)
139        {
140            return 0.0F;
141        }
142
143        if (!canHarvestBlock(block, player, metadata))
144        {
145            float speed = ForgeEventFactory.getBreakSpeed(player, block, metadata, 1.0f);
146            return (speed < 0 ? 0 : speed) / hardness / 100F;
147        }
148        else
149        {
150             return player.getCurrentPlayerStrVsBlock(block, metadata) / hardness / 30F;
151        }
152    }
153
154    public static boolean isToolEffective(ItemStack stack, Block block, int metadata)
155    {
156        List toolClass = toolClasses.get(stack.getItem());
157        return toolClass != null && toolEffectiveness.contains(Arrays.asList(block, metadata, toolClass.get(0)));
158    }
159
160    static void initTools()
161    {
162        if (toolInit)
163        {
164            return;
165        }
166        toolInit = true;
167
168        MinecraftForge.setToolClass(Item.pickaxeWood,    "pickaxe", 0);
169        MinecraftForge.setToolClass(Item.pickaxeStone,   "pickaxe", 1);
170        MinecraftForge.setToolClass(Item.pickaxeSteel,   "pickaxe", 2);
171        MinecraftForge.setToolClass(Item.pickaxeGold,    "pickaxe", 0);
172        MinecraftForge.setToolClass(Item.pickaxeDiamond, "pickaxe", 3);
173
174        MinecraftForge.setToolClass(Item.axeWood,    "axe", 0);
175        MinecraftForge.setToolClass(Item.axeStone,   "axe", 1);
176        MinecraftForge.setToolClass(Item.axeSteel,   "axe", 2);
177        MinecraftForge.setToolClass(Item.axeGold,    "axe", 0);
178        MinecraftForge.setToolClass(Item.axeDiamond, "axe", 3);
179
180        MinecraftForge.setToolClass(Item.shovelWood,    "shovel", 0);
181        MinecraftForge.setToolClass(Item.shovelStone,   "shovel", 1);
182        MinecraftForge.setToolClass(Item.shovelSteel,   "shovel", 2);
183        MinecraftForge.setToolClass(Item.shovelGold,    "shovel", 0);
184        MinecraftForge.setToolClass(Item.shovelDiamond, "shovel", 3);
185
186        for (Block block : ItemPickaxe.blocksEffectiveAgainst)
187        {
188            MinecraftForge.setBlockHarvestLevel(block, "pickaxe", 0);
189        }
190
191        for (Block block : ItemSpade.blocksEffectiveAgainst)
192        {
193            MinecraftForge.setBlockHarvestLevel(block, "shovel", 0);
194        }
195
196        for (Block block : ItemAxe.blocksEffectiveAgainst)
197        {
198            MinecraftForge.setBlockHarvestLevel(block, "axe", 0);
199        }
200
201        MinecraftForge.setBlockHarvestLevel(Block.obsidian,     "pickaxe", 3);
202        MinecraftForge.setBlockHarvestLevel(Block.oreEmerald,   "pickaxe", 2);
203        MinecraftForge.setBlockHarvestLevel(Block.oreDiamond,   "pickaxe", 2);
204        MinecraftForge.setBlockHarvestLevel(Block.blockDiamond, "pickaxe", 2);
205        MinecraftForge.setBlockHarvestLevel(Block.oreGold,      "pickaxe", 2);
206        MinecraftForge.setBlockHarvestLevel(Block.blockGold,    "pickaxe", 2);
207        MinecraftForge.setBlockHarvestLevel(Block.oreIron,      "pickaxe", 1);
208        MinecraftForge.setBlockHarvestLevel(Block.blockSteel,   "pickaxe", 1);
209        MinecraftForge.setBlockHarvestLevel(Block.oreLapis,     "pickaxe", 1);
210        MinecraftForge.setBlockHarvestLevel(Block.blockLapis,   "pickaxe", 1);
211        MinecraftForge.setBlockHarvestLevel(Block.oreRedstone,  "pickaxe", 2);
212        MinecraftForge.setBlockHarvestLevel(Block.oreRedstoneGlowing, "pickaxe", 2);
213        MinecraftForge.removeBlockEffectiveness(Block.oreRedstone, "pickaxe");
214        MinecraftForge.removeBlockEffectiveness(Block.obsidian,    "pickaxe");
215        MinecraftForge.removeBlockEffectiveness(Block.oreRedstoneGlowing, "pickaxe");
216    }
217
218    public static String getTexture(String _default, Object obj)
219    {
220        if (obj instanceof Item)
221        {
222            return ((Item)obj).getTextureFile();
223        }
224        else if (obj instanceof Block)
225        {
226            return ((Block)obj).getTextureFile();
227        }
228        else
229        {
230            return _default;
231        }
232    }
233
234    public static int getTotalArmorValue(EntityPlayer player)
235    {
236        int ret = 0;
237        for (int x = 0; x < player.inventory.armorInventory.length; x++)
238        {
239            ItemStack stack = player.inventory.armorInventory[x];
240            if (stack != null && stack.getItem() instanceof ISpecialArmor)
241            {
242                ret += ((ISpecialArmor)stack.getItem()).getArmorDisplay(player, stack, x);
243            }
244            else if (stack != null && stack.getItem() instanceof ItemArmor)
245            {
246                ret += ((ItemArmor)stack.getItem()).damageReduceAmount;
247            }
248        }
249        return ret;
250    }
251
252    static
253    {
254        grassList.add(new GrassEntry(Block.plantYellow, 0, 20));
255        grassList.add(new GrassEntry(Block.plantRed,    0, 10));
256        seedList.add(new SeedEntry(new ItemStack(Item.seeds), 10));
257        initTools();
258    }
259
260    /**
261     * Called when a player uses 'pick block', calls new Entity and Block hooks.
262     */
263    public static boolean onPickBlock(MovingObjectPosition target, EntityPlayer player, World world)
264    {
265        ItemStack result = null;
266        boolean isCreative = player.capabilities.isCreativeMode;
267
268        if (target.typeOfHit == EnumMovingObjectType.TILE)
269        {
270            int x = target.blockX;
271            int y = target.blockY;
272            int z = target.blockZ;
273            Block var8 = Block.blocksList[world.getBlockId(x, y, z)];
274
275            if (var8 == null)
276            {
277                return false;
278            }
279
280            result = var8.getPickBlock(target, world, x, y, z);
281        }
282        else
283        {
284            if (target.typeOfHit != EnumMovingObjectType.ENTITY || target.entityHit == null || !isCreative)
285            {
286                return false;
287            }
288
289            result = target.entityHit.getPickedResult(target);
290        }
291
292        if (result == null)
293        {
294            return false;
295        }
296
297        for (int x = 0; x < 9; x++)
298        {
299            ItemStack stack = player.inventory.getStackInSlot(x);
300            if (stack != null && stack.isItemEqual(result) && ItemStack.areItemStackTagsEqual(stack, result))
301            {
302                player.inventory.currentItem = x;
303                return true;
304            }
305        }
306
307        if (!isCreative)
308        {
309            return false;
310        }
311
312        int slot = player.inventory.getFirstEmptyStack();
313        if (slot < 0 || slot >= 9)
314        {
315            slot = player.inventory.currentItem;
316        }
317
318        player.inventory.setInventorySlotContents(slot, result);
319        player.inventory.currentItem = slot;
320        return true;
321    }
322
323    //Optifine Helper Functions u.u, these are here specifically for Optifine
324    //Note: When using Optfine, these methods are invoked using reflection, which
325    //incurs a major performance penalty.
326    public static void onLivingSetAttackTarget(EntityLiving entity, EntityLiving target)
327    {
328        MinecraftForge.EVENT_BUS.post(new LivingSetAttackTargetEvent(entity, target));
329    }
330
331    public static boolean onLivingUpdate(EntityLiving entity)
332    {
333        return MinecraftForge.EVENT_BUS.post(new LivingUpdateEvent(entity));
334    }
335
336    public static boolean onLivingAttack(EntityLiving entity, DamageSource src, int amount)
337    {
338        return MinecraftForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount));
339    }
340
341    public static int onLivingHurt(EntityLiving entity, DamageSource src, int amount)
342    {
343        LivingHurtEvent event = new LivingHurtEvent(entity, src, amount);
344        return (MinecraftForge.EVENT_BUS.post(event) ? 0 : event.ammount);
345    }
346
347    public static boolean onLivingDeath(EntityLiving entity, DamageSource src)
348    {
349        return MinecraftForge.EVENT_BUS.post(new LivingDeathEvent(entity, src));
350    }
351
352    public static boolean onLivingDrops(EntityLiving entity, DamageSource source, ArrayList<EntityItem> drops, int lootingLevel, boolean recentlyHit, int specialDropValue)
353    {
354        return MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, source, drops, lootingLevel, recentlyHit, specialDropValue));
355    }
356
357    public static float onLivingFall(EntityLiving entity, float distance)
358    {
359        LivingFallEvent event = new LivingFallEvent(entity, distance);
360        return (MinecraftForge.EVENT_BUS.post(event) ? 0.0f : event.distance);
361    }
362
363    public static boolean isLivingOnLadder(Block block, World world, int x, int y, int z)
364    {
365        return block != null && block.isLadder(world, x, y, z);
366    }
367
368    public static void onLivingJump(EntityLiving entity)
369    {
370        MinecraftForge.EVENT_BUS.post(new LivingJumpEvent(entity));
371    }
372
373    public static EntityItem onPlayerTossEvent(EntityPlayer player, ItemStack item)
374    {
375        player.captureDrops = true;
376        EntityItem ret = player.dropPlayerItemWithRandomChoice(item, false);
377        player.capturedDrops.clear();
378        player.captureDrops = false;
379
380        ItemTossEvent event = new ItemTossEvent(ret, player);
381        if (MinecraftForge.EVENT_BUS.post(event))
382        {
383            return null;
384        }
385
386        player.joinEntityItemWithWorld(event.entityItem);
387        return event.entityItem;
388    }
389}