/******************************************************************************* * AbyssalCraft * Copyright (c) 2012 - 2017 Shinoow. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-3.0.txt * * Contributors: * Shinoow - implementation ******************************************************************************/ package com.shinoow.abyssalcraft.common.entity; import java.util.Iterator; import java.util.List; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityMultiPart; import net.minecraft.entity.MoverType; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.boss.EntityDragonPart; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.ReportedException; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.Explosion; import net.minecraft.world.World; import com.shinoow.abyssalcraft.api.AbyssalCraftAPI; import com.shinoow.abyssalcraft.api.entity.EntityUtil; import com.shinoow.abyssalcraft.api.entity.ICoraliumEntity; import com.shinoow.abyssalcraft.api.item.ACItems; import com.shinoow.abyssalcraft.lib.ACAchievements; import com.shinoow.abyssalcraft.lib.ACConfig; import com.shinoow.abyssalcraft.lib.ACLoot; public class EntityDragonMinion extends EntityMob implements IEntityMultiPart, ICoraliumEntity { public static final float innerRotation = 0; public double targetX; public double targetY; public double targetZ; /** * Ring buffer array for the last 64 Y-positions and yaw rotations. Used to calculate offsets for the animations. */ public double[][] ringBuffer = new double[64][3]; /** * Index into the ring buffer. Incremented once per tick and restarts at 0 once it reaches the end of the buffer. */ public int ringBufferIndex = -1; /** An array containing all body parts of this dragon */ public EntityDragonPart[] dragonPartArray; /** The head bounding box of a dragon */ public EntityDragonPart dragonPartHead; /** The body bounding box of a dragon */ public EntityDragonPart dragonPartBody; public EntityDragonPart dragonPartTail1; public EntityDragonPart dragonPartTail2; public EntityDragonPart dragonPartTail3; public EntityDragonPart dragonPartWing1; public EntityDragonPart dragonPartWing2; /** Animation time at previous tick. */ public float prevAnimTime; /** * Animation time, used to control the speed of the animation cycles (wings flapping, jaw opening, etc.) */ public float animTime; /** Force selecting a new flight target at next tick if set to true. */ public boolean forceNewTarget; private Entity target; public EntityDragonBoss healingcircle; public EntityDragonMinion(World par1World) { super(par1World); dragonPartArray = new EntityDragonPart[] {dragonPartHead = new EntityDragonPart(this, "head", 3.0F, 3.0F), dragonPartBody = new EntityDragonPart(this, "body", 4.0F, 4.0F), dragonPartTail1 = new EntityDragonPart(this, "tail", 2.0F, 2.0F), dragonPartTail2 = new EntityDragonPart(this, "tail", 2.0F, 2.0F), dragonPartTail3 = new EntityDragonPart(this, "tail", 2.0F, 2.0F), dragonPartWing1 = new EntityDragonPart(this, "wing", 2.0F, 2.0F), dragonPartWing2 = new EntityDragonPart(this, "wing", 2.0F, 2.0F)}; setHealth(getMaxHealth()); setSize(8.0F, 3.0F); noClip = true; targetY = 100.0D; isImmuneToFire = true; } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); if(ACConfig.hardcoreMode) getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(60.0D); else getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); } @Override protected void entityInit() { super.entityInit(); } public double[] getMovementOffsets(int par1, float par2) { if (getHealth() <= 0.0F) par2 = 0.0F; par2 = 1.0F - par2; int j = ringBufferIndex - par1 * 1 & 63; int k = ringBufferIndex - par1 * 1 - 1 & 63; double[] adouble = new double[3]; double d0 = ringBuffer[j][0]; double d1 = MathHelper.wrapDegrees(ringBuffer[k][0] - d0); adouble[0] = d0 + d1 * par2; d0 = ringBuffer[j][1]; d1 = ringBuffer[k][1] - d0; adouble[1] = d0 + d1 * par2; adouble[2] = ringBuffer[j][2] + (ringBuffer[k][2] - ringBuffer[j][2]) * par2; return adouble; } @Override protected Item getDropItem() { return ACItems.coralium_plagued_flesh; } @Override protected ResourceLocation getLootTable(){ return ACLoot.ENTITY_SPECTRAL_DRAGON; } @Override public void onDeath(DamageSource par1DamageSource) { super.onDeath(par1DamageSource); if (par1DamageSource.getEntity() instanceof EntityPlayer) { EntityPlayer entityplayer = (EntityPlayer)par1DamageSource.getEntity(); entityplayer.addStat(ACAchievements.kill_spectral_dragon, 1); } } @Override public void onLivingUpdate() { float f; float f1; if (world.isRemote) { f = MathHelper.cos(animTime * (float)Math.PI * 2.0F); f1 = MathHelper.cos(prevAnimTime * (float)Math.PI * 2.0F); if (f1 <= -0.3F && f >= -0.3F) world.playSound(posX, posY, posZ, SoundEvents.ENTITY_ENDERDRAGON_FLAP, getSoundCategory(), 5.0F, 0.8F + rand.nextFloat() * 0.3F, false); } prevAnimTime = animTime; float f2; if (getHealth() <= 0.0F) { f = (rand.nextFloat() - 0.5F) * 8.0F; f1 = (rand.nextFloat() - 0.5F) * 4.0F; f2 = (rand.nextFloat() - 0.5F) * 8.0F; if(ACConfig.particleEntity) world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, posX + f, posY + 2.0D + f1, posZ + f2, 0.0D, 0.0D, 0.0D); } else { updateHealingCircle(); f = 0.2F / (MathHelper.sqrt(motionX * motionX + motionZ * motionZ) * 10.0F + 1.0F); f *= (float)Math.pow(2.0D, motionY); animTime += f; rotationYaw = MathHelper.wrapDegrees(rotationYaw); if (ringBufferIndex < 0) for (int i = 0; i < ringBuffer.length; ++i) { ringBuffer[i][0] = rotationYaw; ringBuffer[i][1] = posY; } if (++ringBufferIndex == ringBuffer.length) ringBufferIndex = 0; ringBuffer[ringBufferIndex][0] = rotationYaw; ringBuffer[ringBufferIndex][1] = posY; double d0; double d1; double d2; double d3; float f3; if (world.isRemote) { if (newPosRotationIncrements > 0) { d3 = posX + (interpTargetX - posX) / newPosRotationIncrements; d0 = posY + (interpTargetY - posY) / newPosRotationIncrements; d1 = posZ + (interpTargetZ - posZ) / newPosRotationIncrements; d2 = MathHelper.wrapDegrees(interpTargetYaw - rotationYaw); rotationYaw = (float)(rotationYaw + d2 / newPosRotationIncrements); rotationPitch = (float)(rotationPitch + (interpTargetPitch - rotationPitch) / newPosRotationIncrements); --newPosRotationIncrements; setPosition(d3, d0, d1); setRotation(rotationYaw, rotationPitch); } } else { d3 = targetX - posX; d0 = targetY - posY; d1 = targetZ - posZ; d2 = d3 * d3 + d0 * d0 + d1 * d1; if (target != null) { targetX = target.posX; targetZ = target.posZ; double d4 = targetX - posX; double d5 = targetZ - posZ; double d6 = Math.sqrt(d4 * d4 + d5 * d5); double d7 = 0.4000000059604645D + d6 / 80.0D - 1.0D; if (d7 > 10.0D) d7 = 10.0D; targetY = target.getEntityBoundingBox().minY + d7; } else { targetX += rand.nextGaussian() * 2.0D; targetZ += rand.nextGaussian() * 2.0D; } if (forceNewTarget || d2 < 100.0D || d2 > 22500.0D || isCollidedHorizontally || isCollidedVertically) setNewTarget(); d0 /= MathHelper.sqrt(d3 * d3 + d1 * d1); f3 = 0.6F; if (d0 < -f3) d0 = -f3; if (d0 > f3) d0 = f3; motionY += d0 * 0.10000000149011612D; rotationYaw = MathHelper.wrapDegrees(rotationYaw); double d8 = 180.0D - Math.atan2(d3, d1) * 180.0D / Math.PI; double d9 = MathHelper.wrapDegrees(d8 - rotationYaw); if (d9 > 50.0D) d9 = 50.0D; if (d9 < -50.0D) d9 = -50.0D; Vec3d vec3 = new Vec3d(targetX - posX, targetY - posY, targetZ - posZ).normalize(); Vec3d vec31 = new Vec3d(MathHelper.sin(rotationYaw * (float)Math.PI / 180.0F), motionY, -MathHelper.cos(rotationYaw * (float)Math.PI / 180.0F)).normalize(); float f4 = (float)(vec31.dotProduct(vec3) + 0.5D) / 1.5F; if (f4 < 0.0F) f4 = 0.0F; randomYawVelocity *= 0.8F; float f5 = MathHelper.sqrt(motionX * motionX + motionZ * motionZ) * 1.0F + 1.0F; double d10 = Math.sqrt(motionX * motionX + motionZ * motionZ) * 1.0D + 1.0D; if (d10 > 40.0D) d10 = 40.0D; randomYawVelocity = (float)(randomYawVelocity + d9 * (0.699999988079071D / d10 / f5)); rotationYaw += randomYawVelocity * 0.1F; float f6 = (float)(2.0D / (d10 + 1.0D)); float f7 = 0.06F; moveRelative(0.0F, -1.0F, f7 * (f4 * f6 + (1.0F - f6))); move(MoverType.SELF, motionX, motionY, motionZ); Vec3d vec32 = new Vec3d(motionX, motionY, motionZ).normalize(); float f8 = (float)(vec32.dotProduct(vec31) + 1.0D) / 2.5F; f8 = 0.8F + 0.15F * f8; motionX *= f8; motionZ *= f8; motionY *= 0.9100000262260437D; } renderYawOffset = rotationYaw; dragonPartHead.width = dragonPartHead.height = 1.5F; dragonPartTail1.width = dragonPartTail1.height = 1.0F; dragonPartTail2.width = dragonPartTail2.height = 1.0F; dragonPartTail3.width = dragonPartTail3.height = 1.0F; dragonPartBody.height = 1.5F; dragonPartBody.width = 2.5F; dragonPartWing1.height = 1.0F; dragonPartWing1.width = 2.0F; dragonPartWing2.height = 1.0F; dragonPartWing2.width = 2.0F; f1 = (float)(getMovementOffsets(5, 1.0F)[1] - getMovementOffsets(10, 1.0F)[1]) * 10.0F / 180.0F * (float)Math.PI; f2 = MathHelper.cos(f1); float f9 = -MathHelper.sin(f1); float f10 = rotationYaw * (float)Math.PI / 180.0F; float f11 = MathHelper.sin(f10); float f12 = MathHelper.cos(f10); dragonPartBody.onUpdate(); dragonPartBody.setLocationAndAngles(posX + f11 * 0.1F, posY, posZ - f12 * 0.1F, 0.0F, 0.0F); dragonPartWing1.onUpdate(); dragonPartWing1.setLocationAndAngles(posX + f12 * 2.2F, posY + 1.0D, posZ + f11 * 2.2F, 0.0F, 0.0F); dragonPartWing2.onUpdate(); dragonPartWing2.setLocationAndAngles(posX - f12 * 2.2F, posY + 1.0D, posZ - f11 * 2.2F, 0.0F, 0.0F); if (!world.isRemote && hurtTime == 0) { collideWithEntities(world.getEntitiesWithinAABBExcludingEntity(this, dragonPartWing1.getEntityBoundingBox().expand(1.0D, 0.5D, 1.0D).offset(0.0D, -0.5D, 0.0D))); collideWithEntities(world.getEntitiesWithinAABBExcludingEntity(this, dragonPartWing2.getEntityBoundingBox().expand(1.0D, 0.5D, 1.0D).offset(0.0D, -0.5D, 0.0D))); attackEntitiesInList(world.getEntitiesWithinAABBExcludingEntity(this, dragonPartHead.getEntityBoundingBox().expand(0.25D, 0.25D, 0.25D))); } double[] adouble = getMovementOffsets(5, 1.0F); double[] adouble1 = getMovementOffsets(0, 1.0F); f3 = MathHelper.sin(rotationYaw * (float)Math.PI / 180.0F - randomYawVelocity * 0.01F); float f13 = MathHelper.cos(rotationYaw * (float)Math.PI / 180.0F - randomYawVelocity * 0.01F); dragonPartHead.onUpdate(); dragonPartHead.setLocationAndAngles(posX + f3 * 2.5F * f2, posY + (adouble1[1] - adouble[1]) * 1.0D + f9 * 2.5F, posZ - f13 * 2.5F * f2, 0.0F, 0.0F); for (int j = 0; j < 3; ++j) { EntityDragonPart entitydragonpart = null; if (j == 0) entitydragonpart = dragonPartTail1; if (j == 1) entitydragonpart = dragonPartTail2; if (j == 2) entitydragonpart = dragonPartTail3; double[] adouble2 = getMovementOffsets(12 + j * 2, 1.0F); float f14 = rotationYaw * (float)Math.PI / 180.0F + simplifyAngle(adouble2[0] - adouble[0]) * (float)Math.PI / 180.0F * 1.0F; float f15 = MathHelper.sin(f14); float f16 = MathHelper.cos(f14); float f17 = 0.1F; float f18 = (j + 1) * 1.5F; entitydragonpart.onUpdate(); entitydragonpart.setLocationAndAngles(posX - (f11 * f17 + f15 * f18) * f2, posY + (adouble2[1] - adouble[1]) * 1.0D - (f18 + f17) * f9 + 1.0D, posZ + (f12 * f17 + f16 * f18) * f2, 0.0F, 0.0F); } } } @Override public void move(MoverType type, double x, double y, double z) { setEntityBoundingBox(getEntityBoundingBox().offset(x, y, z)); resetPositionToBB(); try { doBlockCollisions(); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Checking entity block collision"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being checked for collision"); addEntityCrashInfo(crashreportcategory); throw new ReportedException(crashreport); } } @Override protected void doBlockCollisions() { AxisAlignedBB axisalignedbb = getEntityBoundingBox(); BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = BlockPos.PooledMutableBlockPos.retain(axisalignedbb.minX + 0.001D, axisalignedbb.minY + 0.001D, axisalignedbb.minZ + 0.001D); BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos1 = BlockPos.PooledMutableBlockPos.retain(axisalignedbb.maxX - 0.001D, axisalignedbb.maxY - 0.001D, axisalignedbb.maxZ - 0.001D); BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos2 = BlockPos.PooledMutableBlockPos.retain(); if (world.isAreaLoaded(blockpos$pooledmutableblockpos, blockpos$pooledmutableblockpos1)) for (int i = blockpos$pooledmutableblockpos.getX(); i <= blockpos$pooledmutableblockpos1.getX(); ++i) for (int j = blockpos$pooledmutableblockpos.getY(); j <= blockpos$pooledmutableblockpos1.getY(); ++j) for (int k = blockpos$pooledmutableblockpos.getZ(); k <= blockpos$pooledmutableblockpos1.getZ(); ++k) { blockpos$pooledmutableblockpos2.setPos(i, j, k); IBlockState iblockstate = world.getBlockState(blockpos$pooledmutableblockpos2); if(iblockstate.getMaterial() == Material.PORTAL) addVelocity(motionX > 0 ? -3 : 3, motionY > 0 ? -3 : 3, motionZ > 0 ? -3 : 3); } blockpos$pooledmutableblockpos.release(); blockpos$pooledmutableblockpos1.release(); blockpos$pooledmutableblockpos2.release(); } private void updateHealingCircle() { if (healingcircle != null) if (healingcircle.isDead) { if (!world.isRemote) attackEntityFromPart(dragonPartHead, DamageSource.causeExplosionDamage((Explosion)null), 100.0F); healingcircle = null; } else if (ticksExisted % 10 == 0 && getHealth() <= getMaxHealth()) setHealth(getHealth() - 1.0F); if (rand.nextInt(10) == 0) { float f = 32.0F; List<?> list = world.getEntitiesWithinAABB(EntityDragonBoss.class, getEntityBoundingBox().expand(f, f, f)); EntityDragonBoss entitydragonboss = null; double d0 = Double.MAX_VALUE; Iterator<?> iterator = list.iterator(); while (iterator.hasNext()) { EntityDragonBoss entitydragonboss1 = (EntityDragonBoss)iterator.next(); double d1 = entitydragonboss1.getDistanceSqToEntity(this); if (d1 < d0) { d0 = d1; entitydragonboss = entitydragonboss1; } } healingcircle = entitydragonboss; } } private void collideWithEntities(List<?> par1List) { double d0 = (dragonPartBody.getEntityBoundingBox().minX + dragonPartBody.getEntityBoundingBox().maxX) / 2.0D; double d1 = (dragonPartBody.getEntityBoundingBox().minZ + dragonPartBody.getEntityBoundingBox().maxZ) / 2.0D; Iterator<?> iterator = par1List.iterator(); while (iterator.hasNext()) { Entity entity = (Entity)iterator.next(); if (entity instanceof EntityLivingBase) { double d2 = entity.posX - d0; double d3 = entity.posZ - d1; double d4 = d2 * d2 + d3 * d3; entity.addVelocity(d2 / d4 * 4.0D, 0.20000000298023224D, d3 / d4 * 4.0D); } } } private void attackEntitiesInList(List<?> par1List) { for (int i = 0; i < par1List.size(); ++i) { Entity entity = (Entity)par1List.get(i); if (entity instanceof EntityLivingBase && !EntityUtil.isEntityCoralium((EntityLivingBase)entity)) ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(AbyssalCraftAPI.coralium_plague, 200)); if(ACConfig.hardcoreMode && entity instanceof EntityPlayer) entity.attackEntityFrom(DamageSource.causeMobDamage(this).setDamageBypassesArmor().setDamageIsAbsolute(), 1); } } private void setNewTarget() { forceNewTarget = false; if (rand.nextInt(2) == 0 && !world.playerEntities.isEmpty()) target = world.playerEntities.get(rand.nextInt(world.playerEntities.size())); else { boolean flag = false; do { targetX = 0.0D; targetY = 70.0F + rand.nextFloat() * 50.0F; targetZ = 0.0D; targetX += rand.nextFloat() * 120.0F - 60.0F; targetZ += rand.nextFloat() * 120.0F - 60.0F; double d0 = posX - targetX; double d1 = posY - targetY; double d2 = posZ - targetZ; flag = d0 * d0 + d1 * d1 + d2 * d2 > 100.0D; } while (!flag); target = null; } } private float simplifyAngle(double par1) { return (float)MathHelper.wrapDegrees(par1); } @Override public boolean attackEntityFromPart(EntityDragonPart par1EntityDragonPart, DamageSource par2DamageSource, float par3) { if (par1EntityDragonPart != dragonPartHead) par3 = par3 / 2.0F + 1.0F; float f1 = rotationYaw * (float)Math.PI / 180.0F; float f2 = MathHelper.sin(f1); float f3 = MathHelper.cos(f1); targetX = posX + f2 * 5.0F + (rand.nextFloat() - 0.5F) * 2.0F; targetY = posY + rand.nextFloat() * 3.0F + 1.0D; targetZ = posZ - f3 * 5.0F + (rand.nextFloat() - 0.5F) * 2.0F; target = null; if (par2DamageSource.getEntity() instanceof EntityPlayer || par2DamageSource.isExplosion()) super.attackEntityFrom(par2DamageSource, par3); return true; } @Override public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) { return false; } @Override public Entity[] getParts() { return dragonPartArray; } @Override public boolean canBeCollidedWith() { return false; } @Override public World getWorld() { return world; } @Override protected SoundEvent getAmbientSound() { return SoundEvents.ENTITY_ENDERDRAGON_AMBIENT; } @Override protected SoundEvent getHurtSound() { return SoundEvents.ENTITY_ENDERDRAGON_HURT; } }