/** Copyright (C) <2017> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.item; import java.util.Collection; import java.util.List; import com.google.common.collect.Lists; import net.minecraft.block.material.Material; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.Entity; import net.minecraft.entity.INpc; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.StatCollector; import net.minecraft.world.World; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import zeldaswordskills.api.entity.BombType; import zeldaswordskills.api.entity.CustomExplosion; import zeldaswordskills.api.item.IHandlePickup; import zeldaswordskills.api.item.IHandleToss; import zeldaswordskills.api.item.IUnenchantable; import zeldaswordskills.client.ISwapModel; import zeldaswordskills.client.render.item.ModelItemBomb; import zeldaswordskills.creativetab.ZSSCreativeTabs; import zeldaswordskills.entity.npc.EntityNpcBarnes; import zeldaswordskills.entity.projectile.EntityBomb; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.server.BombTickPacket; import zeldaswordskills.ref.Config; import zeldaswordskills.ref.ModInfo; import zeldaswordskills.ref.Sounds; import zeldaswordskills.util.PlayerUtils; /** * * A bomb that works just like in Zelda games. Throw it quick or it will blow up in your face! * * They can be found in dungeons as well as purchased from blacksmiths (who make them for relatively * cheap) or farmers (who sell them at a higher price). Occasionally dropped from Creepers. * * Incrementing the itemstack's item damage each tick resulted in strange rendering behavior; * storing the time in NBT instead works nicely. * */ public class ItemBomb extends BaseModItem implements IHandlePickup, IHandleToss, ISwapModel, IUnenchantable { /** Blast radius for bombs (Creeper is 3.0F) */ public static final float RADIUS = 3.0F; public ItemBomb() { super(); setMaxDamage(0); setMaxStackSize(1); setHasSubtypes(true); setCreativeTab(ZSSCreativeTabs.tabTools); setFull3D(); } /** Shortcut method; returns this bomb's enum Type from stack damage value */ public static BombType getType(ItemStack stack) { return getType(stack.getItemDamage()); } /** * Returns this bomb's enum Type from stack damage value */ public static BombType getType(int damage) { return (damage < BombType.values().length ? BombType.values()[damage] : BombType.BOMB_STANDARD); } /** * Return blast radius for this bomb type */ public static float getRadius(BombType type) { return (type == BombType.BOMB_WATER ? (RADIUS * 0.75F) : RADIUS); } @Override public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) { if (!world.isRemote) { int fuseTime = (Config.getBombFuseTime() > 0 ? Config.getBombFuseTime() : 56); // subtract any ticks which passed while held fuseTime -= (stack.hasTagCompound() ? stack.getTagCompound().getInteger("time") : 0); world.spawnEntityInWorld(new EntityBomb(world, player).setType(getType(stack)).addTime(fuseTime)); } player.destroyCurrentEquippedItem(); return stack; } @Override public boolean onLeftClickEntity(ItemStack stack, EntityPlayer player, Entity entity) { if (entity instanceof EntityNpcBarnes) { if (!player.worldObj.isRemote) { if (((EntityNpcBarnes) entity).addBombBagTrade()) { PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.bomb.add"); } else { PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.bomb.careful"); } } return true; } else if (entity instanceof INpc) { if (!player.worldObj.isRemote) { PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.bomb.careful"); } return true; } return super.onLeftClickEntity(stack, player, entity); } @Override public boolean onPickupItem(ItemStack stack, EntityPlayer player) { for (ItemStack invStack : player.inventory.mainInventory) { if (invStack != null && invStack.getItem() instanceof ItemBombBag) { stack.stackSize -= (stack.stackSize - ((ItemBombBag) invStack.getItem()).addBombs(invStack, stack)); if (stack.stackSize < 1) { break; } } } return true; } @Override public void onItemTossed(EntityItem item, EntityPlayer player) { ItemStack stack = item.getEntityItem(); if (stack.hasTagCompound() && stack.getTagCompound().hasKey("time")) { stack.getTagCompound().setInteger("time", 0); } } @Override public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) { return !ItemStack.areItemsEqual(oldStack, newStack); } /** * @param slot inventory slot at which the item resides * @param isHeld true if the item is currently held */ @Override public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isHeld) { if (!stack.hasTagCompound()) { stack.setTagCompound(new NBTTagCompound()); stack.getTagCompound().setInteger("time", 0); stack.getTagCompound().setBoolean("inWater", false); } if (isHeld || getType(stack) == BombType.BOMB_FLOWER) { if (entity instanceof EntityPlayer) { if (world.isRemote && Minecraft.getMinecraft().currentScreen == null) { PacketDispatcher.sendToServer(new BombTickPacket(slot)); } } else { tickBomb(stack, world, entity, slot); } } else { stack.getTagCompound().setInteger("time", 0); stack.getTagCompound().setBoolean("inWater", false); } } /** * Increments bomb's timer and causes explosion if time is out; stack must be held by entity */ public void tickBomb(ItemStack stack, World world, Entity entity, int slot) { if (!stack.hasTagCompound()) { stack.setTagCompound(new NBTTagCompound()); stack.getTagCompound().setInteger("time", 0); stack.getTagCompound().setBoolean("inWater", false); } BombType type = getType(stack); if (type != BombType.BOMB_WATER && world.getBlockState(new BlockPos(entity).up()).getBlock().getMaterial() == Material.water) { stack.getTagCompound().setInteger("time", 0); stack.getTagCompound().setBoolean("inWater", true); } else if (stack.getTagCompound().getBoolean("inWater")) { stack.getTagCompound().setBoolean("inWater", false); } if (canTick(world, type, stack.getTagCompound().getBoolean("inWater"))) { int time = stack.getTagCompound().getInteger("time"); if (time % 20 == 0) { world.playSoundAtEntity(entity, Sounds.BOMB_FUSE, 1.0F, 2.0F + entity.worldObj.rand.nextFloat() * 0.4F); } boolean flag = world.provider.getDimensionId() == -1 && (type == BombType.BOMB_STANDARD || type == BombType.BOMB_FLOWER); stack.getTagCompound().setInteger("time", flag ? Config.getBombFuseTime() : ++time); int fuse = Config.getBombFuseTime(); if (fuse == 0 && type == BombType.BOMB_FLOWER) { fuse = 56; } if (time == fuse && !world.isRemote) { EntityBomb bomb = null; if (entity instanceof EntityPlayer) { ((EntityPlayer) entity).inventory.setInventorySlotContents(slot, null); bomb = new EntityBomb(world, (EntityPlayer) entity).setType(type); } else { entity.setCurrentItemOrArmor(slot, null); bomb = new EntityBomb(world).setType(type); } CustomExplosion.createExplosion(bomb, world, entity.posX, entity.posY, entity.posZ, getRadius(type), 0.0F, true); } } } /** * Returns true if this type of bomb can tick this update */ private boolean canTick(World world, BombType type, boolean inWater) { if (type == BombType.BOMB_FLOWER) { return !inWater; } else if (Config.getBombFuseTime() > 0) { switch (type) { case BOMB_WATER: return (world.provider.getDimensionId() != -1); default: return (!inWater); } } else { return false; } } @Override public String getUnlocalizedName(ItemStack stack) { return getUnlocalizedName() + "_" + getType(stack).unlocalizedName; } @Override public String[] getVariants() { String[] variants = new String[BombType.values().length]; for (BombType type : BombType.values()) { variants[type.ordinal()] = ModInfo.ID + ":bomb_" + type.unlocalizedName; } return variants; } @Override @SideOnly(Side.CLIENT) public void registerResources() { super.registerResources(); ModelLoader.registerItemVariants(this, new ModelResourceLocation(ModInfo.ID + ":empty", "inventory")); } @Override @SideOnly(Side.CLIENT) public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) { for (BombType type : BombType.values()) { list.add(new ItemStack(item, 1, type.ordinal())); } } @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack itemstack, EntityPlayer player, List<String> list, boolean advanced) { list.add(EnumChatFormatting.ITALIC + StatCollector.translateToLocal("tooltip.zss.bomb.desc.0")); list.add(EnumChatFormatting.ITALIC + StatCollector.translateToLocal("tooltip.zss.bomb.desc.1")); } @Override @SideOnly(Side.CLIENT) public Collection<ModelResourceLocation> getDefaultResources() { List<ModelResourceLocation> resources = Lists.newArrayList(); for (String s : getVariants()) { resources.add(new ModelResourceLocation(s, "inventory")); } return resources; } @Override @SideOnly(Side.CLIENT) public Class<? extends IBakedModel> getNewModel() { return ModelItemBomb.class; } }