/**
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.entity.mobs;
import java.util.List;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.particle.IParticleFactory;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.monster.EntitySlime;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S04PacketEntityEquipment;
import net.minecraft.util.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSource;
import net.minecraft.util.EntityDamageSourceIndirect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.MathHelper;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.common.eventhandler.Event.Result;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import zeldaswordskills.ClientProxy;
import zeldaswordskills.api.block.IWhipBlock.WhipType;
import zeldaswordskills.api.damage.DamageUtils.DamageSourceIce;
import zeldaswordskills.api.damage.DamageUtils.DamageSourceShock;
import zeldaswordskills.api.damage.IDamageSourceStun;
import zeldaswordskills.api.entity.IEntityBombEater;
import zeldaswordskills.api.entity.IEntityBombIngestible;
import zeldaswordskills.api.entity.IEntityLootable;
import zeldaswordskills.entity.IEntityVariant;
import zeldaswordskills.entity.ZSSEntityInfo;
import zeldaswordskills.entity.buff.Buff;
import zeldaswordskills.item.ItemTreasure.Treasures;
import zeldaswordskills.item.ZSSItems;
import zeldaswordskills.ref.Config;
import zeldaswordskills.ref.Sounds;
import zeldaswordskills.util.BiomeType;
import zeldaswordskills.util.WorldUtils;
import com.google.common.collect.Lists;
/**
*
* Chuchu traits:
*
* All chus are capable of merging back together when injured, making them especially
* challenging to defeat for the unwary.
*
* Red:
* The weakest of the Chu types, but highly resistant to fire. Drops red chu jelly.
* These are most often found in swamps.
*
* Green:
* Slightly stronger than the Red Chu, but no special resistances. Drops green chu jelly.
* These are most often found in plains and are known to cause weakness.
*
* Blue:
* Blue Chus are the rarest type of all, dropping blue chu jelly.
* They are known to occasionally electrify, like the Yellow Chu, are highly resistant to both
* magic and cold, and cause cold damage when not electrified. They are most often found in taiga biomes.
*
* Yellow:
* Yellow chus are the most difficult to defeat, producing an electrical aura when they
* sense danger or take damage. While the aura is active, they chu is immune to all damage
* and attacking it directly will cause shock damage to the attacker. Stun effects and
* explosions are effective in forcing the chu to drop its aura, or one can simply wait.
* Magic damage, however, is able to penetrate their defenses no matter what.
* Drops yellow chu jelly. These are most often found in deserts.
*
*/
public class EntityChu extends EntitySlime implements IEntityBombEater, IEntityLootable, IEntityVariant
{
/** Chuchu types, in order of rarity and strength */
public static enum ChuType {
RED(1, BiomeType.RIVER, BiomeType.FIERY),
GREEN(2, BiomeType.PLAINS, BiomeType.FOREST),
BLUE(3, BiomeType.TAIGA, BiomeType.COLD),
YELLOW(4, BiomeType.ARID, BiomeType.JUNGLE);
/** Modifier for damage, experience, and possibly other things */
public final int modifier;
/** Biome in which this type spawns most frequently (or possibly exclusively) */
public final BiomeType favoredBiome;
/** Secondary biome in which this type spawns most frequently (or possibly exclusively) */
public final BiomeType secondBiome;
private ChuType(int modifier, BiomeType favoredBiome, BiomeType secondBiome) {
this.modifier = modifier;
this.favoredBiome = favoredBiome;
this.secondBiome = secondBiome;
}
/** Returns the Chu type by item damage */
public static final ChuType fromDamage(int damage) {
return ChuType.values()[damage % ChuType.values().length];
}
}
/**
* Returns array of default biomes in which this entity may spawn naturally
*/
public static String[] getDefaultBiomes() {
List<BiomeType> biomes = Lists.newArrayList(BiomeType.BEACH, BiomeType.MOUNTAIN);
for (ChuType type : ChuType.values()) {
biomes.add(type.favoredBiome);
biomes.add(type.secondBiome);
}
return BiomeType.getBiomeArray(null, biomes.toArray(new BiomeType[biomes.size()]));
}
/** Data watcher index for this Chu's type */
private static final int CHU_TYPE_INDEX = 17;
/** Data watcher index for shock time so entity can render appropriately */
private static final int SHOCK_INDEX = 18;
/** Number of times this Chu has merged */
private int timesMerged;
public EntityChu(World world) {
super(world);
setType(ChuType.RED);
}
@Override
protected void entityInit() {
super.entityInit();
dataWatcher.addObject(CHU_TYPE_INDEX, (byte)(ChuType.RED.ordinal()));
dataWatcher.addObject(SHOCK_INDEX, 0);
}
@Override
protected EntityChu createInstance() {
EntityChu chu = new EntityChu(worldObj);
chu.setType(getType());
chu.timesMerged = this.timesMerged;
return chu;
}
@Override
public float getEyeHeight() {
return 0.625F * height;
}
/** Returns this Chu's type */
public ChuType getType() {
return ChuType.values()[dataWatcher.getWatchableObjectByte(CHU_TYPE_INDEX)];
}
/** Sets this Chu's type */
public void setType(ChuType type) {
dataWatcher.updateObject(CHU_TYPE_INDEX, (byte)(type.ordinal()));
applyTypeTraits();
}
@Override
public EntityChu setType(int type) {
setType(ChuType.values()[type % ChuType.values().length]);
return this;
}
private void setTypeOnSpawn() {
if (Config.areMobVariantsAllowed() && rand.nextFloat() < Config.getMobVariantChance()) {
setType(rand.nextInt(ChuType.values().length));
} else {
BiomeGenBase biome = worldObj.getBiomeGenForCoords(new BlockPos(this));
BiomeType biomeType = BiomeType.getBiomeTypeFor(biome);
for (ChuType t : ChuType.values()) {
if (t.favoredBiome == biomeType || t.secondBiome == biomeType) {
setType(t);
return;
}
}
}
}
/**
* Applies traits based on Chu's type
*/
private void applyTypeTraits() {
ZSSEntityInfo info = ZSSEntityInfo.get(this);
info.removeAllBuffs();
switch(getType()) {
case RED:
info.applyBuff(Buff.RESIST_FIRE, Integer.MAX_VALUE, 75);
break;
case BLUE:
info.applyBuff(Buff.RESIST_MAGIC, Integer.MAX_VALUE, 75);
info.applyBuff(Buff.RESIST_COLD, Integer.MAX_VALUE, 100);
info.applyBuff(Buff.RESIST_SHOCK, Integer.MAX_VALUE, 50);
break;
case YELLOW:
info.applyBuff(Buff.RESIST_SHOCK, Integer.MAX_VALUE, 100);
break;
default:
}
}
/** Whether this chu type can shock; always true for Yellow, sometimes true for Blue */
protected boolean canChuTypeShock() {
return (getType() == ChuType.YELLOW || (getType() == ChuType.BLUE && rand.nextInt(80) == 0));
}
/** Returns the amount of time remaining for which this Chu is electrified */
public int getShockTime() {
return dataWatcher.getWatchableObjectInt(SHOCK_INDEX);
}
/** Sets the amount of time this Chu will remain electrified */
public void setShockTime(int time) {
dataWatcher.updateObject(SHOCK_INDEX, time);
}
/** Returns max time affected entities will be stunned when shocked */
protected int getMaxStunTime() {
return (getSlimeSize() * worldObj.getDifficulty().getDifficultyId() * 10);
}
/** Random interval between shocks */
protected int getShockInterval() {
return (getType() == ChuType.YELLOW ? 160 : 320);
}
@Override
protected void setSlimeSize(int size) {
super.setSlimeSize(size);
getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue((double)((size + 1) * (size + 1)));
setHealth(getMaxHealth());
experienceValue += getType().modifier + 1;
}
@Override
public boolean canBreatheUnderwater() {
return true;
}
@Override
public void fall(float distance, float damageMultiplier) {}
@Override
public int getTotalArmorValue() {
return getSlimeSize() + (getType().ordinal() * 2);
}
@Override
protected void dropFewItems(boolean recentlyHit, int looting) {
if (getSlimeSize() > 1) {
int k = rand.nextInt(4) - 2;
if (looting > 0) {
k += rand.nextInt(looting + 1);
}
for (int l = 0; l < k; ++l) {
entityDropItem(new ItemStack(ZSSItems.jellyChu, 1, getType().ordinal()), 0.0F);
}
}
}
@Override
protected void addRandomDrop() {
switch(rand.nextInt(8)) {
case 1: entityDropItem(new ItemStack(ZSSItems.treasure, 1, Treasures.JELLY_BLOB.ordinal()), 0.0F); break;
default: entityDropItem(new ItemStack(rand.nextInt(3) == 1 ? Items.emerald : ZSSItems.smallHeart), 0.0F);
}
}
@Override
public float getLootableChance(EntityPlayer player, WhipType whip) {
return 0.2F;
}
@Override
public ItemStack getEntityLoot(EntityPlayer player, WhipType whip) {
if (rand.nextFloat() < (0.1F * (1 + whip.ordinal()))) {
return new ItemStack(ZSSItems.treasure,1,Treasures.JELLY_BLOB.ordinal());
}
return new ItemStack(ZSSItems.jellyChu, 1, getType().ordinal());
}
@Override
public boolean onLootStolen(EntityPlayer player, boolean wasItemStolen) {
return true;
}
@Override
public boolean isHurtOnTheft(EntityPlayer player, WhipType whip) {
return Config.getHurtOnSteal();
}
@Override
protected String getHurtSound() {
return "mob.slime." + (getSlimeSize() > 1 ? "big" : "small");
}
@Override
protected String getDeathSound() {
return "mob.slime." + (getSlimeSize() > 1 ? "big" : "small");
}
@Override
protected float getSoundVolume() {
return 0.4F * (float) getSlimeSize();
}
@Override
public int getVerticalFaceSpeed() {
return 0;
}
/**
* Returns true if the slime makes a sound when it jumps (based upon the slime's size)
*/
protected boolean makesSoundOnJump() {
return getSlimeSize() > 0;
}
/**
* Returns the name of the sound played when the slime jumps.
*/
protected String getJumpSound() {
return "mob.slime." + (getSlimeSize() > 1 ? "big" : "small");
}
/**
* Whether this chu makes a sound when it lands
*/
protected boolean makesSoundOnLand() {
return getSlimeSize() > 1;
}
@Override
public boolean getCanSpawnHere() {
if (worldObj.getWorldInfo().getTerrainType().handleSlimeSpawnReduction(rand, worldObj)) {
return false;
} else {
BlockPos pos = new BlockPos(this);
if (worldObj.getDifficulty() == EnumDifficulty.PEACEFUL || worldObj.getLightFor(EnumSkyBlock.SKY, pos) > rand.nextInt(32)) {
return false;
}
if (posY > 50.0D && rand.nextFloat() < 0.5F && rand.nextFloat() < worldObj.getCurrentMoonPhaseFactor()) {
int light = worldObj.getLightFromNeighbors(pos);
if (worldObj.isThundering()) {
int j = worldObj.getSkylightSubtracted();
worldObj.setSkylightSubtracted(10);
light = worldObj.getLightFromNeighbors(pos);
worldObj.setSkylightSubtracted(j);
}
return light <= rand.nextInt(8) && super.getCanSpawnHere();
}
Chunk chunk = worldObj.getChunkFromBlockCoords(pos);
if (rand.nextInt(10) == 0 && chunk.getRandomWithSeed(432191789L).nextInt(10) == 0 && posY < 50.0D) {
return super.getCanSpawnHere();
}
return false;
}
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount) {
if (source == DamageSource.inWall) {
return false;
} else if (getShockTime() > 0) {
if (source instanceof EntityDamageSourceIndirect) {
if (source.isMagicDamage()) {
return super.attackEntityFrom(source, amount);
} else if (source.isExplosion()) {
ZSSEntityInfo.get(this).stun(20 + rand.nextInt((int)(amount * 5) + 1));
setShockTime(0);
} else if (source instanceof IDamageSourceStun) {
setShockTime(0);
}
// Hack to prevent infinite loop when attacked by other electrified mobs (other chus, keese, etc)
} else if (source instanceof EntityDamageSource && source.getEntity() instanceof EntityPlayer && !source.damageType.equals("thorns")) {
source.getEntity().attackEntityFrom(getDamageSource(), getAttackStrength());
worldObj.playSoundAtEntity(this, Sounds.SHOCK, 1.0F, 1.0F / (rand.nextFloat() * 0.4F + 1.0F));
}
return false;
}
return super.attackEntityFrom(source, amount);
}
/** The amount of damage this chu will cause when attacking */
@Override
protected int getAttackStrength() {
return super.getAttackStrength() + getType().modifier;
}
/**
* Gets the type-specific damage source, taking shock time into account
*/
private DamageSource getDamageSource() {
if (getShockTime() > 0) {
return new DamageSourceShock("shock", this, getMaxStunTime(), getAttackStrength());
}
switch(getType()) {
case BLUE: return new DamageSourceIce("mob", this, 50, (getSlimeSize() > 2 ? 1 : 0));
default: return new EntityDamageSource("mob", this);
}
}
/**
* Called when slime collides with player
*/
@Override
protected void func_175451_e(EntityLivingBase target) {
int size = getSlimeSize();
double min_distance = 0.6D * 0.6D * size * size;
if (canEntityBeSeen(target) && getDistanceSqToEntity(target) < min_distance && target.attackEntityFrom(getDamageSource(), getAttackStrength())) {
playSound(Sounds.SLIME_ATTACK, 1.0F, (rand.nextFloat() - rand.nextFloat()) * 0.2F + 1.0F);
if (rand.nextFloat() < (0.25F * getSlimeSize())) {
applySecondaryEffects(target);
}
int t = getShockTime();
if (t > 0) {
setShockTime(Math.max(0, t - rand.nextInt(100) - 50));
}
applyEnchantments(this, target);
}
}
/**
* Handles any secondary effects that may occur when the target is damaged by this Chu
*/
private void applySecondaryEffects(EntityLivingBase target) {
switch(getType()) {
case GREEN: ZSSEntityInfo.get(target).applyBuff(Buff.ATTACK_DOWN, 200, 50); break;
case BLUE: ZSSEntityInfo.get(target).applyBuff(Buff.WEAKNESS_COLD, 200, 50); break;
default:
}
}
protected boolean wasOnGround;
@Override
public void onUpdate() {
if (!worldObj.isRemote && worldObj.getDifficulty() == EnumDifficulty.PEACEFUL && getSlimeSize() > 0) {
isDead = true;
}
squishFactor += (squishAmount - squishFactor) * 0.5F;
prevSquishFactor = squishFactor;
// Hack to bypass slime's onUpdate:
if (net.minecraftforge.common.ForgeHooks.onLivingUpdate(this)) return;
entityLivingBaseUpdate();
entityLivingUpdate();
super.onEntityUpdate();
// End hack, start copy/pasta:
if (onGround && !wasOnGround && worldObj.isRemote) {
spawnParticlesOnLanding();
/*
int i = this.getSlimeSize();
for (int j = 0; j < i * 8; ++j)
{
float f = this.rand.nextFloat() * (float)Math.PI * 2.0F;
float f1 = this.rand.nextFloat() * 0.5F + 0.5F;
float f2 = MathHelper.sin(f) * (float)i * 0.5F * f1;
float f3 = MathHelper.cos(f) * (float)i * 0.5F * f1;
World world = this.worldObj;
EnumParticleTypes enumparticletypes = this.func_180487_n();
double d0 = this.posX + (double)f2;
double d1 = this.posZ + (double)f3;
world.spawnParticle(enumparticletypes, d0, this.getEntityBoundingBox().minY, d1, 0.0D, 0.0D, 0.0D, new int[0]);
}
*/
if (makesSoundOnLand()) {
playSound(getJumpSound(), getSoundVolume(), ((rand.nextFloat() - rand.nextFloat()) * 0.2F + 1.0F) / 0.8F);
}
squishAmount = -0.5F;
} else if (!onGround && wasOnGround) {
squishAmount = 1.0F;
}
wasOnGround = onGround;
alterSquishAmount();
if (canChuTypeShock()) {
updateShockState();
}
if (onGround && timesMerged < 4) {
attemptMerge();
}
}
// If only EntitySlime was more inheritance-friendly, e.g. call 'onLanded' to spawn particles, etc.
private ItemStack[] previousEquipment = new ItemStack[5];
private void entityLivingBaseUpdate() {
if (!this.worldObj.isRemote) {
int i = this.getArrowCountInEntity();
if (i > 0) {
if (this.arrowHitTimer <= 0) {
this.arrowHitTimer = 20 * (30 - i);
}
--this.arrowHitTimer;
if (this.arrowHitTimer <= 0) {
this.setArrowCountInEntity(i - 1);
}
}
for (int j = 0; j < 5; ++j) {
ItemStack itemstack = this.previousEquipment[j];
ItemStack itemstack1 = this.getEquipmentInSlot(j);
if (!ItemStack.areItemStacksEqual(itemstack1, itemstack)) {
((WorldServer) worldObj).getEntityTracker().sendToAllTrackingEntity(this, new S04PacketEntityEquipment(this.getEntityId(), j, itemstack1));
if (itemstack != null) {
getAttributeMap().removeAttributeModifiers(itemstack.getAttributeModifiers());
}
if (itemstack1 != null) {
getAttributeMap().applyAttributeModifiers(itemstack1.getAttributeModifiers());
}
this.previousEquipment[j] = itemstack1 == null ? null : itemstack1.copy();
}
}
if (ticksExisted % 20 == 0) {
getCombatTracker().reset();
}
}
this.onLivingUpdate();
double d0 = this.posX - this.prevPosX;
double d1 = this.posZ - this.prevPosZ;
float f = (float)(d0 * d0 + d1 * d1);
float f1 = this.renderYawOffset;
float f2 = 0.0F;
this.prevOnGroundSpeedFactor = this.onGroundSpeedFactor;
float f3 = 0.0F;
if (f > 0.0025000002F) {
f3 = 1.0F;
f2 = (float)Math.sqrt((double)f) * 3.0F;
f1 = (float)Math.atan2(d1, d0) * 180.0F / (float)Math.PI - 90.0F;
}
if (this.swingProgress > 0.0F) {
f1 = this.rotationYaw;
}
if (!this.onGround) {
f3 = 0.0F;
}
this.onGroundSpeedFactor += (f3 - this.onGroundSpeedFactor) * 0.3F;
this.worldObj.theProfiler.startSection("headTurn");
f2 = this.updateDistance(f1, f2);
this.worldObj.theProfiler.endSection();
this.worldObj.theProfiler.startSection("rangeChecks");
while (this.rotationYaw - this.prevRotationYaw < -180.0F) {
this.prevRotationYaw -= 360.0F;
}
while (this.rotationYaw - this.prevRotationYaw >= 180.0F) {
this.prevRotationYaw += 360.0F;
}
while (this.renderYawOffset - this.prevRenderYawOffset < -180.0F) {
this.prevRenderYawOffset -= 360.0F;
}
while (this.renderYawOffset - this.prevRenderYawOffset >= 180.0F) {
this.prevRenderYawOffset += 360.0F;
}
while (this.rotationPitch - this.prevRotationPitch < -180.0F) {
this.prevRotationPitch -= 360.0F;
}
while (this.rotationPitch - this.prevRotationPitch >= 180.0F) {
this.prevRotationPitch += 360.0F;
}
while (this.rotationYawHead - this.prevRotationYawHead < -180.0F) {
this.prevRotationYawHead -= 360.0F;
}
while (this.rotationYawHead - this.prevRotationYawHead >= 180.0F) {
this.prevRotationYawHead += 360.0F;
}
this.worldObj.theProfiler.endSection();
this.movedDistance += f2;
}
private void entityLivingUpdate() {
if (!worldObj.isRemote) {
updateLeashedState();
}
}
/*
@Override
public void onUpdate() {
super.onUpdate();
if (canChuTypeShock()) {
updateShockState();
}
if (onGround && getEntityData().getInteger("timesMerged") < 4) {
attemptMerge();
}
}
*/
/**
* Updates the Chu's shock status; only called if canChuTypeShock() returns true
*/
protected void updateShockState() {
if (getShockTime() == 0 && !ZSSEntityInfo.get(this).isBuffActive(Buff.STUN)) {
EntityPlayer player = worldObj.getClosestPlayerToEntity(this, 16.0D);
if (player != null && (recentlyHit > 0 || rand.nextInt(getShockInterval()) == 0)) {
setShockTime(rand.nextInt(getSlimeSize() * 50) + (worldObj.getDifficulty().getDifficultyId() * (rand.nextInt(20) + 10)));
}
}
if (getShockTime() % 8 > 5 && rand.nextInt(4) == 0) {
worldObj.playSoundAtEntity(this, Sounds.SHOCK, getSoundVolume(), 1.0F / (rand.nextFloat() * 0.4F + 1.0F));
}
int time = getShockTime();
if (time > 0) {
setShockTime(time - 1);
}
}
/**
* Particle to spawn upon landing
* TODO - return custom particle to avoid the stupid onUpdate hack above
*/
@Override
protected EnumParticleTypes getParticleType() {
return super.getParticleType();
}
@SideOnly(Side.CLIENT)
private void spawnParticlesOnLanding() {
int i = getSlimeSize();
float r = 1.0F, g = 1.0F, b = 1.0F;
switch(getType()) {
case RED: r = 0.65F; g = 0.25F; b = 0.3F; break;
case BLUE: r = 0.25F; g = 0.4F; b = 0.75F; break;
case YELLOW: g = 0.65F; b = 0.0F; break;
default:
}
for (int j = 0; j < i * 8; ++j) {
float f = rand.nextFloat() * (float) Math.PI * 2.0F;
float f1 = rand.nextFloat() * 0.5F + 0.5F;
float f2 = MathHelper.sin(f) * (float) i * 0.5F * f1;
float f3 = MathHelper.cos(f) * (float) i * 0.5F * f1;
// Need to use a factory to return the particle without automatically adding it to the effect renderer
IParticleFactory factory = ClientProxy.particleFactoryMap.get(EnumParticleTypes.SLIME.getParticleID());
// Alternate option: EntityBreakingFX.SlimeFactory factory = new EntityBreakingFX.SlimeFactory();
if (factory != null) {
EntityFX particle = factory.getEntityFX(EnumParticleTypes.SLIME.getParticleID(), worldObj, posX + (double) f2, getEntityBoundingBox().minY, posZ + (double) f3, 0, 0, 0);
if (particle != null) {
particle.setRBGColorF(r, g, b);
WorldUtils.spawnWorldParticles(worldObj, particle);
}
}
}
}
private void attemptMerge() {
int i = getSlimeSize();
if (!worldObj.isRemote && i < 3 && getHealth() < (getMaxHealth() / 2) && rand.nextInt(16) == 0) {
List<EntityChu> list = worldObj.getEntitiesWithinAABB(EntityChu.class, getEntityBoundingBox().expand(2.0D, 1.0D, 2.0D));
for (EntityChu chu : list) {
if (chu != this && chu.getSlimeSize() == this.getSlimeSize() && chu.getHealth() < (chu.getMaxHealth() / 2)) {
worldObj.playSoundAtEntity(this, Sounds.CHU_MERGE, 1.0F, 1.0F / (rand.nextFloat() * 0.4F + 1.0F));
EntityChu newChu = createInstance();
newChu.setSlimeSize(i * 2);
newChu.setType(this.getType().ordinal() < chu.getType().ordinal() ? chu.getType() : this.getType());
newChu.setLocationAndAngles((this.posX + chu.posX) / 2, posY + 0.5D, (this.posZ + chu.posZ) / 2 , rand.nextFloat() * 360.0F, 0.0F);
newChu.timesMerged = rand.nextInt(4) + 1 + this.timesMerged;
worldObj.spawnEntityInWorld(newChu);
chu.isDead = true;
this.isDead = true;
break;
}
}
}
}
@Override
public Result ingestBomb(IEntityBombIngestible bomb) {
worldObj.playSoundAtEntity(this, Sounds.CHU_MERGE, 1.0F, 1.0F / (rand.nextFloat() * 0.4F + 1.0F));
return Result.DEFAULT;
}
@Override
public boolean onBombIndigestion(IEntityBombIngestible bomb) {
return true;
}
@Override
public boolean doesIngestedBombExplode(IEntityBombIngestible bomb) {
return true;
}
@Override
public boolean isIngestedBombFatal(IEntityBombIngestible bomb) {
return getSlimeSize() < 4;
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data) {
data = super.onInitialSpawn(difficulty, data);
setTypeOnSpawn();
return data;
}
@Override
public void writeEntityToNBT(NBTTagCompound compound) {
super.writeEntityToNBT(compound);
compound.setInteger("ChuType", getType().ordinal());
compound.setInteger("timesMerged", timesMerged);
}
@Override
public void readEntityFromNBT(NBTTagCompound compound) {
super.readEntityFromNBT(compound);
dataWatcher.updateObject(CHU_TYPE_INDEX, (byte) compound.getInteger("ChuType"));
timesMerged = compound.getInteger("timesMerged");
}
}