/**
Copyright (C) <2015> <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.entity.projectile;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.eventhandler.Event.Result;
import zeldaswordskills.api.entity.BombType;
import zeldaswordskills.api.entity.CustomExplosion;
import zeldaswordskills.api.entity.IEntityBombEater;
import zeldaswordskills.api.entity.IEntityBombIngestible;
import zeldaswordskills.entity.ZSSEntityInfo;
import zeldaswordskills.item.ItemBomb;
import zeldaswordskills.item.ZSSItems;
import zeldaswordskills.ref.Config;
import zeldaswordskills.ref.Sounds;
public class EntityBomb extends EntityMobThrowable implements IEntityBombIngestible
{
/** Time until explosion */
private int fuseTime = 24;
/** Uses ItemBomb's radius if this value is zero */
private float radius = 0.0F;
/** Whether this bomb is capable of destroying blocks */
private boolean canGrief = true;
/** Factor by which affected entity's motion will be multiplied */
protected float motionFactor = 1.0F;
/** Factor by which radius of block destruction is multiplied */
protected float destructionFactor = 1.0F;
/** Watchable object index for bomb's type */
private static final int BOMBTYPE_DATAWATCHER_INDEX = 22;
public EntityBomb(World world) {
super(world);
setGravityVelocity(0.075F);
setSize(0.5F, 0.5F);
}
public EntityBomb(World world, EntityLivingBase entity) {
super(world, entity);
setGravityVelocity(0.075F);
setSize(0.5F, 0.5F);
}
public EntityBomb(World world, double x, double y, double z) {
super(world, x, y, z);
setGravityVelocity(0.075F);
setSize(0.5F, 0.5F);
}
public EntityBomb(World world, EntityLivingBase shooter, EntityLivingBase target, float velocity, float wobble) {
super(world, shooter, target, velocity, wobble);
setGravityVelocity(0.075F);
setSize(0.5F, 0.5F);
}
@Override
public void entityInit() {
super.entityInit();
setDamage(0.0F);
dataWatcher.addObject(BOMBTYPE_DATAWATCHER_INDEX, BombType.BOMB_STANDARD.ordinal());
}
@Override
public float getExplosionDamage(Entity entity) {
return getDamage();
}
@Override
public EntityBomb setExplosionDamage(float damage) {
return (EntityBomb) setDamage(damage);
}
@Override
public float getExplosionRadius(Entity entity) {
return getRadius();
}
@Override
public EntityBomb setExplosionRadius(float radius) {
this.radius = radius;
return this;
}
@Override
public int getFuseTime(Entity entity) {
return fuseTime;
}
@Override
public EntityBomb setFuseTime(int time) {
fuseTime = Math.max(time, 1);
return this;
}
/**
* Adds time to bomb's fuse
*/
public EntityBomb addTime(int time) {
fuseTime = Math.max(fuseTime + time, fuseTime);
return this;
}
public float getRadius() {
return radius > 0 ? radius : ItemBomb.getRadius(getType());
}
@Override
public float getMotionFactor() {
return motionFactor;
}
/**
* Sets amount by which entity's motion will be multiplied
*/
public EntityBomb setMotionFactor(float amount) {
motionFactor = amount;
return this;
}
@Override
public float getDestructionFactor() {
return destructionFactor;
}
/**
* Sets the amount by which block destruction radius will be multiplied
*/
public EntityBomb setDestructionFactor(float factor) {
this.destructionFactor = factor;
return this;
}
// TODO @Override // this is not yet implemented
public boolean canGriefAdventureMode() {
return Config.canGriefAdventure();
}
/**
* Sets this bomb to not destroy blocks, but still cause damage
*/
public EntityBomb setNoGrief() {
canGrief = false;
return this;
}
@Override
public BombType getType() {
return BombType.values()[dataWatcher.getWatchableObjectInt(BOMBTYPE_DATAWATCHER_INDEX)];
}
/**
* Set this bomb's {@link BombType}
*/
public EntityBomb setType(BombType type) {
dataWatcher.updateObject(BOMBTYPE_DATAWATCHER_INDEX, type.ordinal());
return this;
}
@Override
public boolean hasPostExplosionEffect() {
return getType() != BombType.BOMB_FLOWER || getThrower() == null;
}
@Override
protected float getVelocity() {
return 0.5F;
}
@Override
protected boolean canTriggerWalking() {
return false;
}
@Override
public boolean canBeCollidedWith() {
return !isDead;
}
@Override
public AxisAlignedBB getCollisionBox(Entity entity) {
return entity.getEntityBoundingBox();
}
@Override
public void onUpdate() {
super.onUpdate();
prevPosX = posX;
prevPosY = posY;
prevPosZ = posZ;
motionY -= 0.03999999910593033D;
noClip = pushOutOfBlocks(posX, (getEntityBoundingBox().minY + getEntityBoundingBox().maxY) / 2.0D, posZ);
moveEntity(motionX, motionY, motionZ);
float f = 0.98F;
if (onGround) {
f = 0.58800006F;
Block block = worldObj.getBlockState(new BlockPos(this).down()).getBlock();
if (block.getMaterial() != Material.air) {
f = block.slipperiness * 0.98F;
}
}
motionX *= (double) f;
motionY *= 0.9800000190734863D;
motionZ *= (double) f;
if (onGround) {
motionY *= -0.5D;
}
if (!worldObj.isRemote && ticksExisted > 5 && wasBombEaten()) {
setDead(); // make sure it's dead
return;
}
Material material = worldObj.getBlockState(new BlockPos(this)).getBlock().getMaterial();
// func_147470_e is isBoundingBoxBurning
boolean inFire = isBurning() || (material == Material.lava || material == Material.fire) || worldObj.isFlammableWithin(getEntityBoundingBox());
if (isDud(inFire)) {
fuseTime += 10;
disarm(worldObj);
} else if (ticksExisted % 20 == 0) {
playSound(Sounds.BOMB_FUSE, 1.0F, 2.0F + rand.nextFloat() * 0.4F);
}
if (!worldObj.isRemote && shouldExplode(inFire)) {
CustomExplosion.createExplosion(this, worldObj, posX, posY, posZ, getRadius(), getDamage(), canGrief);
setDead();
}
}
/**
* Returns true if any nearby {@link IEntityBombEater} consumes this bomb
*/
private boolean wasBombEaten() {
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, getEntityBoundingBox().expand(0.5D, 0.5D, 0.5D));
for (EntityLivingBase entity : entities) {
if (!entity.isEntityAlive()) {
continue;
}
Result result = (entity instanceof IEntityBombEater ? ((IEntityBombEater) entity).ingestBomb(this) : Result.DENY);
if (result == Result.ALLOW) {
return true;
} else if (result == Result.DEFAULT && ZSSEntityInfo.get(entity).onBombIngested(this)) {
return true;
}
}
return false;
}
/**
* If the bomb is disarmed, replaces the entity with a new EntityItem
*/
public boolean disarm(World world) {
if (!world.isRemote) {
if (isEntityAlive() && fuseTime > 4) {
setDead();
EntityItem bomb = new EntityItem(world, posX, posY, posZ, new ItemStack(ZSSItems.bomb,1,getType().ordinal()));
bomb.setPickupDelay(40);
bomb.motionX = motionX;
bomb.motionY = motionY;
bomb.motionZ = motionZ;
world.spawnEntityInWorld(bomb);
return true;
}
}
return false;
}
@Override
protected void onImpact(MovingObjectPosition movingobjectposition) {
motionX *= 0.5F;
motionY *= -0.5F;
motionZ *= 0.5F;
}
/**
* Returns true if the bomb is a dud: in the water or a water bomb in the nether
* @param inFire whether this bomb is in fire, lava, or currently burning
*/
private boolean isDud(boolean inFire) {
switch(getType()) {
case BOMB_WATER: return inFire || worldObj.provider.getDimensionId() == -1;
default: return (worldObj.getBlockState(new BlockPos(this)).getBlock().getMaterial() == Material.water);
}
}
/**
* Returns whether this bomb should explode
* @param inFire whether this bomb is in fire, lava, or currently burning
*/
private boolean shouldExplode(boolean inFire) {
if (!isEntityAlive()) {
return false;
} else if ((inFire || worldObj.provider.getDimensionId() == -1) && getType() != BombType.BOMB_FIRE) {
return true;
}
return ticksExisted >= fuseTime;
}
@Override
public void writeEntityToNBT(NBTTagCompound compound) {
super.writeEntityToNBT(compound);
compound.setByte("bombType", (byte) getType().ordinal());
compound.setInteger("fuseTime", fuseTime);
compound.setFloat("bombRadius", radius);
compound.setFloat("motionFactor", motionFactor);
compound.setFloat("destructionFactor", destructionFactor);
compound.setBoolean("canGrief", canGrief);
}
@Override
public void readEntityFromNBT(NBTTagCompound compound) {
super.readEntityFromNBT(compound);
setType(BombType.values()[compound.getByte("bombType") % BombType.values().length]);
fuseTime = compound.getInteger("fuseTime");
radius = compound.getFloat("bombRadius");
motionFactor = compound.getFloat("motionFactor");
destructionFactor = compound.getFloat("destructionFactor");
canGrief = compound.getBoolean("canGrief");
}
}