base mod created
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,742 @@
|
||||
package net.minecraft.entity.boss;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackRanged;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.monster.EntityMob;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityWitherSkull;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.BossInfo;
|
||||
import net.minecraft.world.BossInfoServer;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityWither extends EntityMob implements IRangedAttackMob
|
||||
{
|
||||
private static final DataParameter<Integer> FIRST_HEAD_TARGET = EntityDataManager.<Integer>createKey(EntityWither.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Integer> SECOND_HEAD_TARGET = EntityDataManager.<Integer>createKey(EntityWither.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Integer> THIRD_HEAD_TARGET = EntityDataManager.<Integer>createKey(EntityWither.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Integer>[] HEAD_TARGETS = new DataParameter[] {FIRST_HEAD_TARGET, SECOND_HEAD_TARGET, THIRD_HEAD_TARGET};
|
||||
private static final DataParameter<Integer> INVULNERABILITY_TIME = EntityDataManager.<Integer>createKey(EntityWither.class, DataSerializers.VARINT);
|
||||
private final float[] xRotationHeads = new float[2];
|
||||
private final float[] yRotationHeads = new float[2];
|
||||
private final float[] xRotOHeads = new float[2];
|
||||
private final float[] yRotOHeads = new float[2];
|
||||
private final int[] nextHeadUpdate = new int[2];
|
||||
private final int[] idleHeadUpdates = new int[2];
|
||||
/** Time before the Wither tries to break blocks */
|
||||
private int blockBreakCounter;
|
||||
private final BossInfoServer bossInfo = (BossInfoServer)(new BossInfoServer(this.getDisplayName(), BossInfo.Color.PURPLE, BossInfo.Overlay.PROGRESS)).setDarkenSky(true);
|
||||
/** Selector used to determine the entities a wither boss should attack. */
|
||||
private static final Predicate<Entity> NOT_UNDEAD = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ instanceof EntityLivingBase && ((EntityLivingBase)p_apply_1_).getCreatureAttribute() != EnumCreatureAttribute.UNDEAD && ((EntityLivingBase)p_apply_1_).attackable();
|
||||
}
|
||||
};
|
||||
|
||||
public EntityWither(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setHealth(this.getMaxHealth());
|
||||
this.setSize(0.9F, 3.5F);
|
||||
this.isImmuneToFire = true;
|
||||
((PathNavigateGround)this.getNavigator()).setCanSwim(true);
|
||||
this.experienceValue = 50;
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(0, new EntityWither.AIDoNothing());
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIAttackRanged(this, 1.0D, 40, 20.0F));
|
||||
this.tasks.addTask(5, new EntityAIWanderAvoidWater(this, 1.0D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(7, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, false, NOT_UNDEAD));
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(FIRST_HEAD_TARGET, Integer.valueOf(0));
|
||||
this.dataManager.register(SECOND_HEAD_TARGET, Integer.valueOf(0));
|
||||
this.dataManager.register(THIRD_HEAD_TARGET, Integer.valueOf(0));
|
||||
this.dataManager.register(INVULNERABILITY_TIME, Integer.valueOf(0));
|
||||
}
|
||||
|
||||
public static void registerFixesWither(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityWither.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("Invul", this.getInvulTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.setInvulTime(compound.getInteger("Invul"));
|
||||
|
||||
if (this.hasCustomName())
|
||||
{
|
||||
this.bossInfo.setName(this.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom name tag for this entity
|
||||
*/
|
||||
public void setCustomNameTag(String name)
|
||||
{
|
||||
super.setCustomNameTag(name);
|
||||
this.bossInfo.setName(this.getDisplayName());
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_DEATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
|
||||
* use this to react to sunlight and start to burn.
|
||||
*/
|
||||
public void onLivingUpdate()
|
||||
{
|
||||
this.motionY *= 0.6000000238418579D;
|
||||
|
||||
if (!this.world.isRemote && this.getWatchedTargetId(0) > 0)
|
||||
{
|
||||
Entity entity = this.world.getEntityByID(this.getWatchedTargetId(0));
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
if (this.posY < entity.posY || !this.isArmored() && this.posY < entity.posY + 5.0D)
|
||||
{
|
||||
if (this.motionY < 0.0D)
|
||||
{
|
||||
this.motionY = 0.0D;
|
||||
}
|
||||
|
||||
this.motionY += (0.5D - this.motionY) * 0.6000000238418579D;
|
||||
}
|
||||
|
||||
double d0 = entity.posX - this.posX;
|
||||
double d1 = entity.posZ - this.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1;
|
||||
|
||||
if (d3 > 9.0D)
|
||||
{
|
||||
double d5 = (double)MathHelper.sqrt(d3);
|
||||
this.motionX += (d0 / d5 * 0.5D - this.motionX) * 0.6000000238418579D;
|
||||
this.motionZ += (d1 / d5 * 0.5D - this.motionZ) * 0.6000000238418579D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.motionX * this.motionX + this.motionZ * this.motionZ > 0.05000000074505806D)
|
||||
{
|
||||
this.rotationYaw = (float)MathHelper.atan2(this.motionZ, this.motionX) * (180F / (float)Math.PI) - 90.0F;
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
this.yRotOHeads[i] = this.yRotationHeads[i];
|
||||
this.xRotOHeads[i] = this.xRotationHeads[i];
|
||||
}
|
||||
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
int k = this.getWatchedTargetId(j + 1);
|
||||
Entity entity1 = null;
|
||||
|
||||
if (k > 0)
|
||||
{
|
||||
entity1 = this.world.getEntityByID(k);
|
||||
}
|
||||
|
||||
if (entity1 != null)
|
||||
{
|
||||
double d11 = this.getHeadX(j + 1);
|
||||
double d12 = this.getHeadY(j + 1);
|
||||
double d13 = this.getHeadZ(j + 1);
|
||||
double d6 = entity1.posX - d11;
|
||||
double d7 = entity1.posY + (double)entity1.getEyeHeight() - d12;
|
||||
double d8 = entity1.posZ - d13;
|
||||
double d9 = (double)MathHelper.sqrt(d6 * d6 + d8 * d8);
|
||||
float f = (float)(MathHelper.atan2(d8, d6) * (180D / Math.PI)) - 90.0F;
|
||||
float f1 = (float)(-(MathHelper.atan2(d7, d9) * (180D / Math.PI)));
|
||||
this.xRotationHeads[j] = this.rotlerp(this.xRotationHeads[j], f1, 40.0F);
|
||||
this.yRotationHeads[j] = this.rotlerp(this.yRotationHeads[j], f, 10.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.yRotationHeads[j] = this.rotlerp(this.yRotationHeads[j], this.renderYawOffset, 10.0F);
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag = this.isArmored();
|
||||
|
||||
for (int l = 0; l < 3; ++l)
|
||||
{
|
||||
double d10 = this.getHeadX(l);
|
||||
double d2 = this.getHeadY(l);
|
||||
double d4 = this.getHeadZ(l);
|
||||
this.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, d10 + this.rand.nextGaussian() * 0.30000001192092896D, d2 + this.rand.nextGaussian() * 0.30000001192092896D, d4 + this.rand.nextGaussian() * 0.30000001192092896D, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
if (flag && this.world.rand.nextInt(4) == 0)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, d10 + this.rand.nextGaussian() * 0.30000001192092896D, d2 + this.rand.nextGaussian() * 0.30000001192092896D, d4 + this.rand.nextGaussian() * 0.30000001192092896D, 0.699999988079071D, 0.699999988079071D, 0.5D);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getInvulTime() > 0)
|
||||
{
|
||||
for (int i1 = 0; i1 < 3; ++i1)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, this.posX + this.rand.nextGaussian(), this.posY + (double)(this.rand.nextFloat() * 3.3F), this.posZ + this.rand.nextGaussian(), 0.699999988079071D, 0.699999988079071D, 0.8999999761581421D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
if (this.getInvulTime() > 0)
|
||||
{
|
||||
int j1 = this.getInvulTime() - 1;
|
||||
|
||||
if (j1 <= 0)
|
||||
{
|
||||
this.world.newExplosion(this, this.posX, this.posY + (double)this.getEyeHeight(), this.posZ, 7.0F, false, net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this));
|
||||
this.world.playBroadcastSound(1023, new BlockPos(this), 0);
|
||||
}
|
||||
|
||||
this.setInvulTime(j1);
|
||||
|
||||
if (this.ticksExisted % 10 == 0)
|
||||
{
|
||||
this.heal(10.0F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.updateAITasks();
|
||||
|
||||
for (int i = 1; i < 3; ++i)
|
||||
{
|
||||
if (this.ticksExisted >= this.nextHeadUpdate[i - 1])
|
||||
{
|
||||
this.nextHeadUpdate[i - 1] = this.ticksExisted + 10 + this.rand.nextInt(10);
|
||||
|
||||
if (this.world.getDifficulty() == EnumDifficulty.NORMAL || this.world.getDifficulty() == EnumDifficulty.HARD)
|
||||
{
|
||||
int j3 = i - 1;
|
||||
int k3 = this.idleHeadUpdates[i - 1];
|
||||
this.idleHeadUpdates[j3] = this.idleHeadUpdates[i - 1] + 1;
|
||||
|
||||
if (k3 > 15)
|
||||
{
|
||||
float f = 10.0F;
|
||||
float f1 = 5.0F;
|
||||
double d0 = MathHelper.nextDouble(this.rand, this.posX - 10.0D, this.posX + 10.0D);
|
||||
double d1 = MathHelper.nextDouble(this.rand, this.posY - 5.0D, this.posY + 5.0D);
|
||||
double d2 = MathHelper.nextDouble(this.rand, this.posZ - 10.0D, this.posZ + 10.0D);
|
||||
this.launchWitherSkullToCoords(i + 1, d0, d1, d2, true);
|
||||
this.idleHeadUpdates[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int k1 = this.getWatchedTargetId(i);
|
||||
|
||||
if (k1 > 0)
|
||||
{
|
||||
Entity entity = this.world.getEntityByID(k1);
|
||||
|
||||
if (entity != null && entity.isEntityAlive() && this.getDistanceSq(entity) <= 900.0D && this.canEntityBeSeen(entity))
|
||||
{
|
||||
if (entity instanceof EntityPlayer && ((EntityPlayer)entity).capabilities.disableDamage)
|
||||
{
|
||||
this.updateWatchedTargetId(i, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.launchWitherSkullToEntity(i + 1, (EntityLivingBase)entity);
|
||||
this.nextHeadUpdate[i - 1] = this.ticksExisted + 40 + this.rand.nextInt(20);
|
||||
this.idleHeadUpdates[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updateWatchedTargetId(i, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EntityLivingBase> list = this.world.<EntityLivingBase>getEntitiesWithinAABB(EntityLivingBase.class, this.getEntityBoundingBox().grow(20.0D, 8.0D, 20.0D), Predicates.and(NOT_UNDEAD, EntitySelectors.NOT_SPECTATING));
|
||||
|
||||
for (int j2 = 0; j2 < 10 && !list.isEmpty(); ++j2)
|
||||
{
|
||||
EntityLivingBase entitylivingbase = list.get(this.rand.nextInt(list.size()));
|
||||
|
||||
if (entitylivingbase != this && entitylivingbase.isEntityAlive() && this.canEntityBeSeen(entitylivingbase))
|
||||
{
|
||||
if (entitylivingbase instanceof EntityPlayer)
|
||||
{
|
||||
if (!((EntityPlayer)entitylivingbase).capabilities.disableDamage)
|
||||
{
|
||||
this.updateWatchedTargetId(i, entitylivingbase.getEntityId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updateWatchedTargetId(i, entitylivingbase.getEntityId());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
list.remove(entitylivingbase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getAttackTarget() != null)
|
||||
{
|
||||
this.updateWatchedTargetId(0, this.getAttackTarget().getEntityId());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updateWatchedTargetId(0, 0);
|
||||
}
|
||||
|
||||
if (this.blockBreakCounter > 0)
|
||||
{
|
||||
--this.blockBreakCounter;
|
||||
|
||||
if (this.blockBreakCounter == 0 && net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this))
|
||||
{
|
||||
int i1 = MathHelper.floor(this.posY);
|
||||
int l1 = MathHelper.floor(this.posX);
|
||||
int i2 = MathHelper.floor(this.posZ);
|
||||
boolean flag = false;
|
||||
|
||||
for (int k2 = -1; k2 <= 1; ++k2)
|
||||
{
|
||||
for (int l2 = -1; l2 <= 1; ++l2)
|
||||
{
|
||||
for (int j = 0; j <= 3; ++j)
|
||||
{
|
||||
int i3 = l1 + k2;
|
||||
int k = i1 + j;
|
||||
int l = i2 + l2;
|
||||
BlockPos blockpos = new BlockPos(i3, k, l);
|
||||
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
||||
Block block = iblockstate.getBlock();
|
||||
|
||||
if (!block.isAir(iblockstate, this.world, blockpos) && block.canEntityDestroy(iblockstate, world, blockpos, this) && net.minecraftforge.event.ForgeEventFactory.onEntityDestroyBlock(this, blockpos, iblockstate))
|
||||
{
|
||||
flag = this.world.destroyBlock(blockpos, true) || flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.world.playEvent((EntityPlayer)null, 1022, new BlockPos(this), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.ticksExisted % 20 == 0)
|
||||
{
|
||||
this.heal(1.0F);
|
||||
}
|
||||
|
||||
this.bossInfo.setPercent(this.getHealth() / this.getMaxHealth());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canDestroyBlock(Block blockIn)
|
||||
{
|
||||
return blockIn != Blocks.BEDROCK && blockIn != Blocks.END_PORTAL && blockIn != Blocks.END_PORTAL_FRAME && blockIn != Blocks.COMMAND_BLOCK && blockIn != Blocks.REPEATING_COMMAND_BLOCK && blockIn != Blocks.CHAIN_COMMAND_BLOCK && blockIn != Blocks.BARRIER && blockIn != Blocks.STRUCTURE_BLOCK && blockIn != Blocks.STRUCTURE_VOID && blockIn != Blocks.PISTON_EXTENSION && blockIn != Blocks.END_GATEWAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this Wither's explosion sequence and makes it invulnerable. Called immediately after spawning.
|
||||
*/
|
||||
public void ignite()
|
||||
{
|
||||
this.setInvulTime(220);
|
||||
this.setHealth(this.getMaxHealth() / 3.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Entity inside a web block.
|
||||
*/
|
||||
public void setInWeb()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given player to the list of players tracking this entity. For instance, a player may track a boss in
|
||||
* order to view its associated boss bar.
|
||||
*/
|
||||
public void addTrackingPlayer(EntityPlayerMP player)
|
||||
{
|
||||
super.addTrackingPlayer(player);
|
||||
this.bossInfo.addPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given player from the list of players tracking this entity. See {@link Entity#addTrackingPlayer} for
|
||||
* more information on tracking.
|
||||
*/
|
||||
public void removeTrackingPlayer(EntityPlayerMP player)
|
||||
{
|
||||
super.removeTrackingPlayer(player);
|
||||
this.bossInfo.removePlayer(player);
|
||||
}
|
||||
|
||||
private double getHeadX(int p_82214_1_)
|
||||
{
|
||||
if (p_82214_1_ <= 0)
|
||||
{
|
||||
return this.posX;
|
||||
}
|
||||
else
|
||||
{
|
||||
float f = (this.renderYawOffset + (float)(180 * (p_82214_1_ - 1))) * 0.017453292F;
|
||||
float f1 = MathHelper.cos(f);
|
||||
return this.posX + (double)f1 * 1.3D;
|
||||
}
|
||||
}
|
||||
|
||||
private double getHeadY(int p_82208_1_)
|
||||
{
|
||||
return p_82208_1_ <= 0 ? this.posY + 3.0D : this.posY + 2.2D;
|
||||
}
|
||||
|
||||
private double getHeadZ(int p_82213_1_)
|
||||
{
|
||||
if (p_82213_1_ <= 0)
|
||||
{
|
||||
return this.posZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
float f = (this.renderYawOffset + (float)(180 * (p_82213_1_ - 1))) * 0.017453292F;
|
||||
float f1 = MathHelper.sin(f);
|
||||
return this.posZ + (double)f1 * 1.3D;
|
||||
}
|
||||
}
|
||||
|
||||
private float rotlerp(float p_82204_1_, float p_82204_2_, float p_82204_3_)
|
||||
{
|
||||
float f = MathHelper.wrapDegrees(p_82204_2_ - p_82204_1_);
|
||||
|
||||
if (f > p_82204_3_)
|
||||
{
|
||||
f = p_82204_3_;
|
||||
}
|
||||
|
||||
if (f < -p_82204_3_)
|
||||
{
|
||||
f = -p_82204_3_;
|
||||
}
|
||||
|
||||
return p_82204_1_ + f;
|
||||
}
|
||||
|
||||
private void launchWitherSkullToEntity(int p_82216_1_, EntityLivingBase p_82216_2_)
|
||||
{
|
||||
this.launchWitherSkullToCoords(p_82216_1_, p_82216_2_.posX, p_82216_2_.posY + (double)p_82216_2_.getEyeHeight() * 0.5D, p_82216_2_.posZ, p_82216_1_ == 0 && this.rand.nextFloat() < 0.001F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches a Wither skull toward (par2, par4, par6)
|
||||
*/
|
||||
private void launchWitherSkullToCoords(int p_82209_1_, double x, double y, double z, boolean invulnerable)
|
||||
{
|
||||
this.world.playEvent((EntityPlayer)null, 1024, new BlockPos(this), 0);
|
||||
double d0 = this.getHeadX(p_82209_1_);
|
||||
double d1 = this.getHeadY(p_82209_1_);
|
||||
double d2 = this.getHeadZ(p_82209_1_);
|
||||
double d3 = x - d0;
|
||||
double d4 = y - d1;
|
||||
double d5 = z - d2;
|
||||
EntityWitherSkull entitywitherskull = new EntityWitherSkull(this.world, this, d3, d4, d5);
|
||||
|
||||
if (invulnerable)
|
||||
{
|
||||
entitywitherskull.setInvulnerable(true);
|
||||
}
|
||||
|
||||
entitywitherskull.posY = d1;
|
||||
entitywitherskull.posX = d0;
|
||||
entitywitherskull.posZ = d2;
|
||||
this.world.spawnEntity(entitywitherskull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack the specified entity using a ranged attack.
|
||||
*/
|
||||
public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor)
|
||||
{
|
||||
this.launchWitherSkullToEntity(0, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isEntityInvulnerable(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (source != DamageSource.DROWN && !(source.getTrueSource() instanceof EntityWither))
|
||||
{
|
||||
if (this.getInvulTime() > 0 && source != DamageSource.OUT_OF_WORLD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.isArmored())
|
||||
{
|
||||
Entity entity = source.getImmediateSource();
|
||||
|
||||
if (entity instanceof EntityArrow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Entity entity1 = source.getTrueSource();
|
||||
|
||||
if (entity1 != null && !(entity1 instanceof EntityPlayer) && entity1 instanceof EntityLivingBase && ((EntityLivingBase)entity1).getCreatureAttribute() == this.getCreatureAttribute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.blockBreakCounter <= 0)
|
||||
{
|
||||
this.blockBreakCounter = 20;
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.idleHeadUpdates.length; ++i)
|
||||
{
|
||||
this.idleHeadUpdates[i] += 3;
|
||||
}
|
||||
|
||||
return super.attackEntityFrom(source, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop 0-2 items of this living's type
|
||||
*/
|
||||
protected void dropFewItems(boolean wasRecentlyHit, int lootingModifier)
|
||||
{
|
||||
EntityItem entityitem = this.dropItem(Items.NETHER_STAR, 1);
|
||||
|
||||
if (entityitem != null)
|
||||
{
|
||||
entityitem.setNoDespawn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the entity despawn if requirements are reached
|
||||
*/
|
||||
protected void despawnEntity()
|
||||
{
|
||||
this.idleTime = 0;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getBrightnessForRender()
|
||||
{
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
public void fall(float distance, float damageMultiplier)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a PotionEffect to the entity
|
||||
*/
|
||||
public void addPotionEffect(PotionEffect potioneffectIn)
|
||||
{
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(300.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.6000000238418579D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(40.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue(4.0D);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getHeadYRotation(int p_82207_1_)
|
||||
{
|
||||
return this.yRotationHeads[p_82207_1_];
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getHeadXRotation(int p_82210_1_)
|
||||
{
|
||||
return this.xRotationHeads[p_82210_1_];
|
||||
}
|
||||
|
||||
public int getInvulTime()
|
||||
{
|
||||
return ((Integer)this.dataManager.get(INVULNERABILITY_TIME)).intValue();
|
||||
}
|
||||
|
||||
public void setInvulTime(int time)
|
||||
{
|
||||
this.dataManager.set(INVULNERABILITY_TIME, Integer.valueOf(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target entity ID if present, or -1 if not @param par1 The target offset, should be from 0-2
|
||||
*/
|
||||
public int getWatchedTargetId(int head)
|
||||
{
|
||||
return ((Integer)this.dataManager.get(HEAD_TARGETS[head])).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target entity ID
|
||||
*/
|
||||
public void updateWatchedTargetId(int targetOffset, int newId)
|
||||
{
|
||||
this.dataManager.set(HEAD_TARGETS[targetOffset], Integer.valueOf(newId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the wither is armored with its boss armor or not by checking whether its health is below half of
|
||||
* its maximum.
|
||||
*/
|
||||
public boolean isArmored()
|
||||
{
|
||||
return this.getHealth() <= this.getMaxHealth() / 2.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.UNDEAD;
|
||||
}
|
||||
|
||||
protected boolean canBeRidden(Entity entityIn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if this Entity is a boss, true otherwise.
|
||||
*/
|
||||
public boolean isNonBoss()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSwingingArms(boolean swingingArms)
|
||||
{
|
||||
}
|
||||
|
||||
class AIDoNothing extends EntityAIBase
|
||||
{
|
||||
public AIDoNothing()
|
||||
{
|
||||
this.setMutexBits(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return EntityWither.this.getInvulTime() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.MultiPartEntityPart;
|
||||
import net.minecraft.entity.item.EntityEnderCrystal;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public interface IPhase
|
||||
{
|
||||
boolean getIsStationary();
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
void doClientRenderEffects();
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
void doLocalUpdate();
|
||||
|
||||
void onCrystalDestroyed(EntityEnderCrystal crystal, BlockPos pos, DamageSource dmgSrc, @Nullable EntityPlayer plyr);
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
void initPhase();
|
||||
|
||||
void removeAreaEffect();
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
float getMaxRiseOrFall();
|
||||
|
||||
float getYawFactor();
|
||||
|
||||
PhaseList <? extends IPhase > getType();
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
Vec3d getTargetLocation();
|
||||
|
||||
/**
|
||||
* Normally, just returns damage. If dragon is sitting and src is an arrow, arrow is enflamed and zero damage
|
||||
* returned.
|
||||
*/
|
||||
float getAdjustedDamage(MultiPartEntityPart pt, DamageSource src, float damage);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.MultiPartEntityPart;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.entity.item.EntityEnderCrystal;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public abstract class PhaseBase implements IPhase
|
||||
{
|
||||
protected final EntityDragon dragon;
|
||||
|
||||
public PhaseBase(EntityDragon dragonIn)
|
||||
{
|
||||
this.dragon = dragonIn;
|
||||
}
|
||||
|
||||
public boolean getIsStationary()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
public void doClientRenderEffects()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public void onCrystalDestroyed(EntityEnderCrystal crystal, BlockPos pos, DamageSource dmgSrc, @Nullable EntityPlayer plyr)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
}
|
||||
|
||||
public void removeAreaEffect()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
public float getMaxRiseOrFall()
|
||||
{
|
||||
return 0.6F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, just returns damage. If dragon is sitting and src is an arrow, arrow is enflamed and zero damage
|
||||
* returned.
|
||||
*/
|
||||
public float getAdjustedDamage(MultiPartEntityPart pt, DamageSource src, float damage)
|
||||
{
|
||||
return damage;
|
||||
}
|
||||
|
||||
public float getYawFactor()
|
||||
{
|
||||
float f = MathHelper.sqrt(this.dragon.motionX * this.dragon.motionX + this.dragon.motionZ * this.dragon.motionZ) + 1.0F;
|
||||
float f1 = Math.min(f, 40.0F);
|
||||
return 0.7F / f1 / f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class PhaseChargingPlayer extends PhaseBase
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private Vec3d targetLocation;
|
||||
private int timeSinceCharge;
|
||||
|
||||
public PhaseChargingPlayer(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (this.targetLocation == null)
|
||||
{
|
||||
LOGGER.warn("Aborting charge player as no target was set.");
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.HOLDING_PATTERN);
|
||||
}
|
||||
else if (this.timeSinceCharge > 0 && this.timeSinceCharge++ >= 10)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.HOLDING_PATTERN);
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
|
||||
if (d0 < 100.0D || d0 > 22500.0D || this.dragon.collidedHorizontally || this.dragon.collidedVertically)
|
||||
{
|
||||
++this.timeSinceCharge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.targetLocation = null;
|
||||
this.timeSinceCharge = 0;
|
||||
}
|
||||
|
||||
public void setTarget(Vec3d p_188668_1_)
|
||||
{
|
||||
this.targetLocation = p_188668_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
public float getMaxRiseOrFall()
|
||||
{
|
||||
return 3.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseChargingPlayer> getType()
|
||||
{
|
||||
return PhaseList.CHARGING_PLAYER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.gen.feature.WorldGenEndPodium;
|
||||
|
||||
public class PhaseDying extends PhaseBase
|
||||
{
|
||||
private Vec3d targetLocation;
|
||||
private int time;
|
||||
|
||||
public PhaseDying(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
public void doClientRenderEffects()
|
||||
{
|
||||
if (this.time++ % 10 == 0)
|
||||
{
|
||||
float f = (this.dragon.getRNG().nextFloat() - 0.5F) * 8.0F;
|
||||
float f1 = (this.dragon.getRNG().nextFloat() - 0.5F) * 4.0F;
|
||||
float f2 = (this.dragon.getRNG().nextFloat() - 0.5F) * 8.0F;
|
||||
this.dragon.world.spawnParticle(EnumParticleTypes.EXPLOSION_HUGE, this.dragon.posX + (double)f, this.dragon.posY + 2.0D + (double)f1, this.dragon.posZ + (double)f2, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
++this.time;
|
||||
|
||||
if (this.targetLocation == null)
|
||||
{
|
||||
BlockPos blockpos = this.dragon.world.getHeight(WorldGenEndPodium.END_PODIUM_LOCATION);
|
||||
this.targetLocation = new Vec3d((double)blockpos.getX(), (double)blockpos.getY(), (double)blockpos.getZ());
|
||||
}
|
||||
|
||||
double d0 = this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
|
||||
if (d0 >= 100.0D && d0 <= 22500.0D && !this.dragon.collidedHorizontally && !this.dragon.collidedVertically)
|
||||
{
|
||||
this.dragon.setHealth(1.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dragon.setHealth(0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.targetLocation = null;
|
||||
this.time = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
public float getMaxRiseOrFall()
|
||||
{
|
||||
return 3.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseDying> getType()
|
||||
{
|
||||
return PhaseList.DYING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.entity.item.EntityEnderCrystal;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.gen.feature.WorldGenEndPodium;
|
||||
|
||||
public class PhaseHoldingPattern extends PhaseBase
|
||||
{
|
||||
private Path currentPath;
|
||||
private Vec3d targetLocation;
|
||||
private boolean clockwise;
|
||||
|
||||
public PhaseHoldingPattern(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
public PhaseList<PhaseHoldingPattern> getType()
|
||||
{
|
||||
return PhaseList.HOLDING_PATTERN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
double d0 = this.targetLocation == null ? 0.0D : this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
|
||||
if (d0 < 100.0D || d0 > 22500.0D || this.dragon.collidedHorizontally || this.dragon.collidedVertically)
|
||||
{
|
||||
this.findNewTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.currentPath = null;
|
||||
this.targetLocation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
private void findNewTarget()
|
||||
{
|
||||
if (this.currentPath != null && this.currentPath.isFinished())
|
||||
{
|
||||
BlockPos blockpos = this.dragon.world.getTopSolidOrLiquidBlock(new BlockPos(WorldGenEndPodium.END_PODIUM_LOCATION));
|
||||
int i = this.dragon.getFightManager() == null ? 0 : this.dragon.getFightManager().getNumAliveCrystals();
|
||||
|
||||
if (this.dragon.getRNG().nextInt(i + 3) == 0)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.LANDING_APPROACH);
|
||||
return;
|
||||
}
|
||||
|
||||
double d0 = 64.0D;
|
||||
EntityPlayer entityplayer = this.dragon.world.getNearestAttackablePlayer(blockpos, d0, d0);
|
||||
|
||||
if (entityplayer != null)
|
||||
{
|
||||
d0 = entityplayer.getDistanceSqToCenter(blockpos) / 512.0D;
|
||||
}
|
||||
|
||||
if (entityplayer != null && (this.dragon.getRNG().nextInt(MathHelper.abs((int)d0) + 2) == 0 || this.dragon.getRNG().nextInt(i + 2) == 0))
|
||||
{
|
||||
this.strafePlayer(entityplayer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentPath == null || this.currentPath.isFinished())
|
||||
{
|
||||
int j = this.dragon.initPathPoints();
|
||||
int k = j;
|
||||
|
||||
if (this.dragon.getRNG().nextInt(8) == 0)
|
||||
{
|
||||
this.clockwise = !this.clockwise;
|
||||
k = j + 6;
|
||||
}
|
||||
|
||||
if (this.clockwise)
|
||||
{
|
||||
++k;
|
||||
}
|
||||
else
|
||||
{
|
||||
--k;
|
||||
}
|
||||
|
||||
if (this.dragon.getFightManager() != null && this.dragon.getFightManager().getNumAliveCrystals() >= 0)
|
||||
{
|
||||
k = k % 12;
|
||||
|
||||
if (k < 0)
|
||||
{
|
||||
k += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = k - 12;
|
||||
k = k & 7;
|
||||
k = k + 12;
|
||||
}
|
||||
|
||||
this.currentPath = this.dragon.findPath(j, k, (PathPoint)null);
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
}
|
||||
}
|
||||
|
||||
this.navigateToNextPathNode();
|
||||
}
|
||||
|
||||
private void strafePlayer(EntityPlayer player)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.STRAFE_PLAYER);
|
||||
((PhaseStrafePlayer)this.dragon.getPhaseManager().getPhase(PhaseList.STRAFE_PLAYER)).setTarget(player);
|
||||
}
|
||||
|
||||
private void navigateToNextPathNode()
|
||||
{
|
||||
if (this.currentPath != null && !this.currentPath.isFinished())
|
||||
{
|
||||
Vec3d vec3d = this.currentPath.getCurrentPos();
|
||||
this.currentPath.incrementPathIndex();
|
||||
double d0 = vec3d.x;
|
||||
double d1 = vec3d.z;
|
||||
double d2;
|
||||
|
||||
while (true)
|
||||
{
|
||||
d2 = vec3d.y + (double)(this.dragon.getRNG().nextFloat() * 20.0F);
|
||||
|
||||
if (d2 >= vec3d.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.targetLocation = new Vec3d(d0, d2, d1);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCrystalDestroyed(EntityEnderCrystal crystal, BlockPos pos, DamageSource dmgSrc, @Nullable EntityPlayer plyr)
|
||||
{
|
||||
if (plyr != null && !plyr.capabilities.disableDamage)
|
||||
{
|
||||
this.strafePlayer(plyr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class PhaseHover extends PhaseBase
|
||||
{
|
||||
private Vec3d targetLocation;
|
||||
|
||||
public PhaseHover(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (this.targetLocation == null)
|
||||
{
|
||||
this.targetLocation = new Vec3d(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getIsStationary()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.targetLocation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
public float getMaxRiseOrFall()
|
||||
{
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseHover> getType()
|
||||
{
|
||||
return PhaseList.HOVER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.gen.feature.WorldGenEndPodium;
|
||||
|
||||
public class PhaseLanding extends PhaseBase
|
||||
{
|
||||
private Vec3d targetLocation;
|
||||
|
||||
public PhaseLanding(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
public void doClientRenderEffects()
|
||||
{
|
||||
Vec3d vec3d = this.dragon.getHeadLookVec(1.0F).normalize();
|
||||
vec3d.rotateYaw(-((float)Math.PI / 4F));
|
||||
double d0 = this.dragon.dragonPartHead.posX;
|
||||
double d1 = this.dragon.dragonPartHead.posY + (double)(this.dragon.dragonPartHead.height / 2.0F);
|
||||
double d2 = this.dragon.dragonPartHead.posZ;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
double d3 = d0 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
double d4 = d1 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
double d5 = d2 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
this.dragon.world.spawnParticle(EnumParticleTypes.DRAGON_BREATH, d3, d4, d5, -vec3d.x * 0.07999999821186066D + this.dragon.motionX, -vec3d.y * 0.30000001192092896D + this.dragon.motionY, -vec3d.z * 0.07999999821186066D + this.dragon.motionZ);
|
||||
vec3d.rotateYaw(0.19634955F);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (this.targetLocation == null)
|
||||
{
|
||||
this.targetLocation = new Vec3d(this.dragon.world.getTopSolidOrLiquidBlock(WorldGenEndPodium.END_PODIUM_LOCATION));
|
||||
}
|
||||
|
||||
if (this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ) < 1.0D)
|
||||
{
|
||||
((PhaseSittingFlaming)this.dragon.getPhaseManager().getPhase(PhaseList.SITTING_FLAMING)).resetFlameCount();
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.SITTING_SCANNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount dragon may rise or fall during this phase
|
||||
*/
|
||||
public float getMaxRiseOrFall()
|
||||
{
|
||||
return 1.5F;
|
||||
}
|
||||
|
||||
public float getYawFactor()
|
||||
{
|
||||
float f = MathHelper.sqrt(this.dragon.motionX * this.dragon.motionX + this.dragon.motionZ * this.dragon.motionZ) + 1.0F;
|
||||
float f1 = Math.min(f, 40.0F);
|
||||
return f1 / f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.targetLocation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseLanding> getType()
|
||||
{
|
||||
return PhaseList.LANDING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.gen.feature.WorldGenEndPodium;
|
||||
|
||||
public class PhaseLandingApproach extends PhaseBase
|
||||
{
|
||||
private Path currentPath;
|
||||
private Vec3d targetLocation;
|
||||
|
||||
public PhaseLandingApproach(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
public PhaseList<PhaseLandingApproach> getType()
|
||||
{
|
||||
return PhaseList.LANDING_APPROACH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.currentPath = null;
|
||||
this.targetLocation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
double d0 = this.targetLocation == null ? 0.0D : this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
|
||||
if (d0 < 100.0D || d0 > 22500.0D || this.dragon.collidedHorizontally || this.dragon.collidedVertically)
|
||||
{
|
||||
this.findNewTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
private void findNewTarget()
|
||||
{
|
||||
if (this.currentPath == null || this.currentPath.isFinished())
|
||||
{
|
||||
int i = this.dragon.initPathPoints();
|
||||
BlockPos blockpos = this.dragon.world.getTopSolidOrLiquidBlock(WorldGenEndPodium.END_PODIUM_LOCATION);
|
||||
EntityPlayer entityplayer = this.dragon.world.getNearestAttackablePlayer(blockpos, 128.0D, 128.0D);
|
||||
int j;
|
||||
|
||||
if (entityplayer != null)
|
||||
{
|
||||
Vec3d vec3d = (new Vec3d(entityplayer.posX, 0.0D, entityplayer.posZ)).normalize();
|
||||
j = this.dragon.getNearestPpIdx(-vec3d.x * 40.0D, 105.0D, -vec3d.z * 40.0D);
|
||||
}
|
||||
else
|
||||
{
|
||||
j = this.dragon.getNearestPpIdx(40.0D, (double)blockpos.getY(), 0.0D);
|
||||
}
|
||||
|
||||
PathPoint pathpoint = new PathPoint(blockpos.getX(), blockpos.getY(), blockpos.getZ());
|
||||
this.currentPath = this.dragon.findPath(i, j, pathpoint);
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
}
|
||||
}
|
||||
|
||||
this.navigateToNextPathNode();
|
||||
|
||||
if (this.currentPath != null && this.currentPath.isFinished())
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.LANDING);
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToNextPathNode()
|
||||
{
|
||||
if (this.currentPath != null && !this.currentPath.isFinished())
|
||||
{
|
||||
Vec3d vec3d = this.currentPath.getCurrentPos();
|
||||
this.currentPath.incrementPathIndex();
|
||||
double d0 = vec3d.x;
|
||||
double d1 = vec3d.z;
|
||||
double d2;
|
||||
|
||||
while (true)
|
||||
{
|
||||
d2 = vec3d.y + (double)(this.dragon.getRNG().nextFloat() * 20.0F);
|
||||
|
||||
if (d2 >= vec3d.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.targetLocation = new Vec3d(d0, d2, d1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
|
||||
public class PhaseList<T extends IPhase>
|
||||
{
|
||||
private static PhaseList<?>[] phases = new PhaseList[0];
|
||||
public static final PhaseList<PhaseHoldingPattern> HOLDING_PATTERN = create(PhaseHoldingPattern.class, "HoldingPattern");
|
||||
public static final PhaseList<PhaseStrafePlayer> STRAFE_PLAYER = create(PhaseStrafePlayer.class, "StrafePlayer");
|
||||
public static final PhaseList<PhaseLandingApproach> LANDING_APPROACH = create(PhaseLandingApproach.class, "LandingApproach");
|
||||
public static final PhaseList<PhaseLanding> LANDING = create(PhaseLanding.class, "Landing");
|
||||
public static final PhaseList<PhaseTakeoff> TAKEOFF = create(PhaseTakeoff.class, "Takeoff");
|
||||
public static final PhaseList<PhaseSittingFlaming> SITTING_FLAMING = create(PhaseSittingFlaming.class, "SittingFlaming");
|
||||
public static final PhaseList<PhaseSittingScanning> SITTING_SCANNING = create(PhaseSittingScanning.class, "SittingScanning");
|
||||
public static final PhaseList<PhaseSittingAttacking> SITTING_ATTACKING = create(PhaseSittingAttacking.class, "SittingAttacking");
|
||||
public static final PhaseList<PhaseChargingPlayer> CHARGING_PLAYER = create(PhaseChargingPlayer.class, "ChargingPlayer");
|
||||
public static final PhaseList<PhaseDying> DYING = create(PhaseDying.class, "Dying");
|
||||
public static final PhaseList<PhaseHover> HOVER = create(PhaseHover.class, "Hover");
|
||||
private final Class <? extends IPhase > clazz;
|
||||
private final int id;
|
||||
private final String name;
|
||||
|
||||
private PhaseList(int idIn, Class <? extends IPhase > clazzIn, String nameIn)
|
||||
{
|
||||
this.id = idIn;
|
||||
this.clazz = clazzIn;
|
||||
this.name = nameIn;
|
||||
}
|
||||
|
||||
public IPhase createPhase(EntityDragon dragon)
|
||||
{
|
||||
try
|
||||
{
|
||||
Constructor <? extends IPhase > constructor = this.getConstructor();
|
||||
return constructor.newInstance(dragon);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new Error(exception);
|
||||
}
|
||||
}
|
||||
|
||||
protected Constructor <? extends IPhase > getConstructor() throws NoSuchMethodException
|
||||
{
|
||||
return this.clazz.getConstructor(EntityDragon.class);
|
||||
}
|
||||
|
||||
public int getId()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.name + " (#" + this.id + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a phase by its ID. If the phase is out of bounds (negative or beyond the end of the phase array), returns
|
||||
* {@link #HOLDING_PATTERN}.
|
||||
*/
|
||||
public static PhaseList<?> getById(int idIn)
|
||||
{
|
||||
return idIn >= 0 && idIn < phases.length ? phases[idIn] : HOLDING_PATTERN;
|
||||
}
|
||||
|
||||
public static int getTotalPhases()
|
||||
{
|
||||
return phases.length;
|
||||
}
|
||||
|
||||
private static <T extends IPhase> PhaseList<T> create(Class<T> phaseIn, String nameIn)
|
||||
{
|
||||
PhaseList<T> phaselist = new PhaseList<T>(phases.length, phaseIn, nameIn);
|
||||
phases = (PhaseList[])Arrays.copyOf(phases, phases.length + 1);
|
||||
phases[phaselist.getId()] = phaselist;
|
||||
return phaselist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class PhaseManager
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final EntityDragon dragon;
|
||||
private final IPhase[] phases = new IPhase[PhaseList.getTotalPhases()];
|
||||
private IPhase phase;
|
||||
|
||||
public PhaseManager(EntityDragon dragonIn)
|
||||
{
|
||||
this.dragon = dragonIn;
|
||||
this.setPhase(PhaseList.HOVER);
|
||||
}
|
||||
|
||||
public void setPhase(PhaseList<?> phaseIn)
|
||||
{
|
||||
if (this.phase == null || phaseIn != this.phase.getType())
|
||||
{
|
||||
if (this.phase != null)
|
||||
{
|
||||
this.phase.removeAreaEffect();
|
||||
}
|
||||
|
||||
this.phase = this.getPhase(phaseIn);
|
||||
|
||||
if (!this.dragon.world.isRemote)
|
||||
{
|
||||
this.dragon.getDataManager().set(EntityDragon.PHASE, Integer.valueOf(phaseIn.getId()));
|
||||
}
|
||||
|
||||
LOGGER.debug("Dragon is now in phase {} on the {}", phaseIn, this.dragon.world.isRemote ? "client" : "server");
|
||||
this.phase.initPhase();
|
||||
}
|
||||
}
|
||||
|
||||
public IPhase getCurrentPhase()
|
||||
{
|
||||
return this.phase;
|
||||
}
|
||||
|
||||
public <T extends IPhase> T getPhase(PhaseList<T> phaseIn)
|
||||
{
|
||||
int i = phaseIn.getId();
|
||||
|
||||
if (this.phases[i] == null)
|
||||
{
|
||||
this.phases[i] = phaseIn.createPhase(this.dragon);
|
||||
}
|
||||
|
||||
return (T)this.phases[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
|
||||
public class PhaseSittingAttacking extends PhaseSittingBase
|
||||
{
|
||||
private int attackingTicks;
|
||||
|
||||
public PhaseSittingAttacking(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
public void doClientRenderEffects()
|
||||
{
|
||||
this.dragon.world.playSound(this.dragon.posX, this.dragon.posY, this.dragon.posZ, SoundEvents.ENTITY_ENDERDRAGON_GROWL, this.dragon.getSoundCategory(), 2.5F, 0.8F + this.dragon.getRNG().nextFloat() * 0.3F, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (this.attackingTicks++ >= 40)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.SITTING_FLAMING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.attackingTicks = 0;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseSittingAttacking> getType()
|
||||
{
|
||||
return PhaseList.SITTING_ATTACKING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import net.minecraft.entity.MultiPartEntityPart;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.util.DamageSource;
|
||||
|
||||
public abstract class PhaseSittingBase extends PhaseBase
|
||||
{
|
||||
public PhaseSittingBase(EntityDragon p_i46794_1_)
|
||||
{
|
||||
super(p_i46794_1_);
|
||||
}
|
||||
|
||||
public boolean getIsStationary()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, just returns damage. If dragon is sitting and src is an arrow, arrow is enflamed and zero damage
|
||||
* returned.
|
||||
*/
|
||||
public float getAdjustedDamage(MultiPartEntityPart pt, DamageSource src, float damage)
|
||||
{
|
||||
if (src.getImmediateSource() instanceof EntityArrow)
|
||||
{
|
||||
src.getImmediateSource().setFire(1);
|
||||
return 0.0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.getAdjustedDamage(pt, src, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import net.minecraft.entity.EntityAreaEffectCloud;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class PhaseSittingFlaming extends PhaseSittingBase
|
||||
{
|
||||
private int flameTicks;
|
||||
private int flameCount;
|
||||
private EntityAreaEffectCloud areaEffectCloud;
|
||||
|
||||
public PhaseSittingFlaming(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates particle effects appropriate to the phase (or sometimes sounds).
|
||||
* Called by dragon's onLivingUpdate. Only used when worldObj.isRemote.
|
||||
*/
|
||||
public void doClientRenderEffects()
|
||||
{
|
||||
++this.flameTicks;
|
||||
|
||||
if (this.flameTicks % 2 == 0 && this.flameTicks < 10)
|
||||
{
|
||||
Vec3d vec3d = this.dragon.getHeadLookVec(1.0F).normalize();
|
||||
vec3d.rotateYaw(-((float)Math.PI / 4F));
|
||||
double d0 = this.dragon.dragonPartHead.posX;
|
||||
double d1 = this.dragon.dragonPartHead.posY + (double)(this.dragon.dragonPartHead.height / 2.0F);
|
||||
double d2 = this.dragon.dragonPartHead.posZ;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
double d3 = d0 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
double d4 = d1 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
double d5 = d2 + this.dragon.getRNG().nextGaussian() / 2.0D;
|
||||
|
||||
for (int j = 0; j < 6; ++j)
|
||||
{
|
||||
this.dragon.world.spawnParticle(EnumParticleTypes.DRAGON_BREATH, d3, d4, d5, -vec3d.x * 0.07999999821186066D * (double)j, -vec3d.y * 0.6000000238418579D, -vec3d.z * 0.07999999821186066D * (double)j);
|
||||
}
|
||||
|
||||
vec3d.rotateYaw(0.19634955F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
++this.flameTicks;
|
||||
|
||||
if (this.flameTicks >= 200)
|
||||
{
|
||||
if (this.flameCount >= 4)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.TAKEOFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.SITTING_SCANNING);
|
||||
}
|
||||
}
|
||||
else if (this.flameTicks == 10)
|
||||
{
|
||||
Vec3d vec3d = (new Vec3d(this.dragon.dragonPartHead.posX - this.dragon.posX, 0.0D, this.dragon.dragonPartHead.posZ - this.dragon.posZ)).normalize();
|
||||
float f = 5.0F;
|
||||
double d0 = this.dragon.dragonPartHead.posX + vec3d.x * 5.0D / 2.0D;
|
||||
double d1 = this.dragon.dragonPartHead.posZ + vec3d.z * 5.0D / 2.0D;
|
||||
double d2 = this.dragon.dragonPartHead.posY + (double)(this.dragon.dragonPartHead.height / 2.0F);
|
||||
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(MathHelper.floor(d0), MathHelper.floor(d2), MathHelper.floor(d1));
|
||||
|
||||
while (this.dragon.world.isAirBlock(blockpos$mutableblockpos) && d2 >= 0) //Forge: Fix infinite loop if ground is missing.
|
||||
{
|
||||
--d2;
|
||||
blockpos$mutableblockpos.setPos(MathHelper.floor(d0), MathHelper.floor(d2), MathHelper.floor(d1));
|
||||
}
|
||||
|
||||
d2 = (double)(MathHelper.floor(d2) + 1);
|
||||
this.areaEffectCloud = new EntityAreaEffectCloud(this.dragon.world, d0, d2, d1);
|
||||
this.areaEffectCloud.setOwner(this.dragon);
|
||||
this.areaEffectCloud.setRadius(5.0F);
|
||||
this.areaEffectCloud.setDuration(200);
|
||||
this.areaEffectCloud.setParticle(EnumParticleTypes.DRAGON_BREATH);
|
||||
this.areaEffectCloud.addEffect(new PotionEffect(MobEffects.INSTANT_DAMAGE));
|
||||
this.dragon.world.spawnEntity(this.areaEffectCloud);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.flameTicks = 0;
|
||||
++this.flameCount;
|
||||
}
|
||||
|
||||
public void removeAreaEffect()
|
||||
{
|
||||
if (this.areaEffectCloud != null)
|
||||
{
|
||||
this.areaEffectCloud.setDead();
|
||||
this.areaEffectCloud = null;
|
||||
}
|
||||
}
|
||||
|
||||
public PhaseList<PhaseSittingFlaming> getType()
|
||||
{
|
||||
return PhaseList.SITTING_FLAMING;
|
||||
}
|
||||
|
||||
public void resetFlameCount()
|
||||
{
|
||||
this.flameCount = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class PhaseSittingScanning extends PhaseSittingBase
|
||||
{
|
||||
private int scanningTime;
|
||||
|
||||
public PhaseSittingScanning(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
++this.scanningTime;
|
||||
EntityLivingBase entitylivingbase = this.dragon.world.getNearestAttackablePlayer(this.dragon, 20.0D, 10.0D);
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
if (this.scanningTime > 25)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.SITTING_ATTACKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3d vec3d = (new Vec3d(entitylivingbase.posX - this.dragon.posX, 0.0D, entitylivingbase.posZ - this.dragon.posZ)).normalize();
|
||||
Vec3d vec3d1 = (new Vec3d((double)MathHelper.sin(this.dragon.rotationYaw * 0.017453292F), 0.0D, (double)(-MathHelper.cos(this.dragon.rotationYaw * 0.017453292F)))).normalize();
|
||||
float f = (float)vec3d1.dotProduct(vec3d);
|
||||
float f1 = (float)(Math.acos((double)f) * (180D / Math.PI)) + 0.5F;
|
||||
|
||||
if (f1 < 0.0F || f1 > 10.0F)
|
||||
{
|
||||
double d0 = entitylivingbase.posX - this.dragon.dragonPartHead.posX;
|
||||
double d1 = entitylivingbase.posZ - this.dragon.dragonPartHead.posZ;
|
||||
double d2 = MathHelper.clamp(MathHelper.wrapDegrees(180.0D - MathHelper.atan2(d0, d1) * (180D / Math.PI) - (double)this.dragon.rotationYaw), -100.0D, 100.0D);
|
||||
this.dragon.randomYawVelocity *= 0.8F;
|
||||
float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1) + 1.0F;
|
||||
float f3 = f2;
|
||||
|
||||
if (f2 > 40.0F)
|
||||
{
|
||||
f2 = 40.0F;
|
||||
}
|
||||
|
||||
this.dragon.randomYawVelocity = (float)((double)this.dragon.randomYawVelocity + d2 * (double)(0.7F / f2 / f3));
|
||||
this.dragon.rotationYaw += this.dragon.randomYawVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.scanningTime >= 100)
|
||||
{
|
||||
entitylivingbase = this.dragon.world.getNearestAttackablePlayer(this.dragon, 150.0D, 150.0D);
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.TAKEOFF);
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.CHARGING_PLAYER);
|
||||
((PhaseChargingPlayer)this.dragon.getPhaseManager().getPhase(PhaseList.CHARGING_PLAYER)).setTarget(new Vec3d(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.scanningTime = 0;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseSittingScanning> getType()
|
||||
{
|
||||
return PhaseList.SITTING_SCANNING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityDragonFireball;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class PhaseStrafePlayer extends PhaseBase
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private int fireballCharge;
|
||||
private Path currentPath;
|
||||
private Vec3d targetLocation;
|
||||
private EntityLivingBase attackTarget;
|
||||
private boolean holdingPatternClockwise;
|
||||
|
||||
public PhaseStrafePlayer(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (this.attackTarget == null)
|
||||
{
|
||||
LOGGER.warn("Skipping player strafe phase because no player was found");
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.HOLDING_PATTERN);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.currentPath != null && this.currentPath.isFinished())
|
||||
{
|
||||
double d0 = this.attackTarget.posX;
|
||||
double d1 = this.attackTarget.posZ;
|
||||
double d2 = d0 - this.dragon.posX;
|
||||
double d3 = d1 - this.dragon.posZ;
|
||||
double d4 = (double)MathHelper.sqrt(d2 * d2 + d3 * d3);
|
||||
double d5 = Math.min(0.4000000059604645D + d4 / 80.0D - 1.0D, 10.0D);
|
||||
this.targetLocation = new Vec3d(d0, this.attackTarget.posY + d5, d1);
|
||||
}
|
||||
|
||||
double d12 = this.targetLocation == null ? 0.0D : this.targetLocation.squareDistanceTo(this.dragon.posX, this.dragon.posY, this.dragon.posZ);
|
||||
|
||||
if (d12 < 100.0D || d12 > 22500.0D)
|
||||
{
|
||||
this.findNewTarget();
|
||||
}
|
||||
|
||||
double d13 = 64.0D;
|
||||
|
||||
if (this.attackTarget.getDistanceSq(this.dragon) < 4096.0D)
|
||||
{
|
||||
if (this.dragon.canEntityBeSeen(this.attackTarget))
|
||||
{
|
||||
++this.fireballCharge;
|
||||
Vec3d vec3d1 = (new Vec3d(this.attackTarget.posX - this.dragon.posX, 0.0D, this.attackTarget.posZ - this.dragon.posZ)).normalize();
|
||||
Vec3d vec3d = (new Vec3d((double)MathHelper.sin(this.dragon.rotationYaw * 0.017453292F), 0.0D, (double)(-MathHelper.cos(this.dragon.rotationYaw * 0.017453292F)))).normalize();
|
||||
float f1 = (float)vec3d.dotProduct(vec3d1);
|
||||
float f = (float)(Math.acos((double)f1) * (180D / Math.PI));
|
||||
f = f + 0.5F;
|
||||
|
||||
if (this.fireballCharge >= 5 && f >= 0.0F && f < 10.0F)
|
||||
{
|
||||
double d14 = 1.0D;
|
||||
Vec3d vec3d2 = this.dragon.getLook(1.0F);
|
||||
double d6 = this.dragon.dragonPartHead.posX - vec3d2.x * 1.0D;
|
||||
double d7 = this.dragon.dragonPartHead.posY + (double)(this.dragon.dragonPartHead.height / 2.0F) + 0.5D;
|
||||
double d8 = this.dragon.dragonPartHead.posZ - vec3d2.z * 1.0D;
|
||||
double d9 = this.attackTarget.posX - d6;
|
||||
double d10 = this.attackTarget.posY + (double)(this.attackTarget.height / 2.0F) - (d7 + (double)(this.dragon.dragonPartHead.height / 2.0F));
|
||||
double d11 = this.attackTarget.posZ - d8;
|
||||
this.dragon.world.playEvent((EntityPlayer)null, 1017, new BlockPos(this.dragon), 0);
|
||||
EntityDragonFireball entitydragonfireball = new EntityDragonFireball(this.dragon.world, this.dragon, d9, d10, d11);
|
||||
entitydragonfireball.setLocationAndAngles(d6, d7, d8, 0.0F, 0.0F);
|
||||
this.dragon.world.spawnEntity(entitydragonfireball);
|
||||
this.fireballCharge = 0;
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
while (!this.currentPath.isFinished())
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
}
|
||||
}
|
||||
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.HOLDING_PATTERN);
|
||||
}
|
||||
}
|
||||
else if (this.fireballCharge > 0)
|
||||
{
|
||||
--this.fireballCharge;
|
||||
}
|
||||
}
|
||||
else if (this.fireballCharge > 0)
|
||||
{
|
||||
--this.fireballCharge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findNewTarget()
|
||||
{
|
||||
if (this.currentPath == null || this.currentPath.isFinished())
|
||||
{
|
||||
int i = this.dragon.initPathPoints();
|
||||
int j = i;
|
||||
|
||||
if (this.dragon.getRNG().nextInt(8) == 0)
|
||||
{
|
||||
this.holdingPatternClockwise = !this.holdingPatternClockwise;
|
||||
j = i + 6;
|
||||
}
|
||||
|
||||
if (this.holdingPatternClockwise)
|
||||
{
|
||||
++j;
|
||||
}
|
||||
else
|
||||
{
|
||||
--j;
|
||||
}
|
||||
|
||||
if (this.dragon.getFightManager() != null && this.dragon.getFightManager().getNumAliveCrystals() > 0)
|
||||
{
|
||||
j = j % 12;
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
j += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
j = j - 12;
|
||||
j = j & 7;
|
||||
j = j + 12;
|
||||
}
|
||||
|
||||
this.currentPath = this.dragon.findPath(i, j, (PathPoint)null);
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
}
|
||||
}
|
||||
|
||||
this.navigateToNextPathNode();
|
||||
}
|
||||
|
||||
private void navigateToNextPathNode()
|
||||
{
|
||||
if (this.currentPath != null && !this.currentPath.isFinished())
|
||||
{
|
||||
Vec3d vec3d = this.currentPath.getCurrentPos();
|
||||
this.currentPath.incrementPathIndex();
|
||||
double d0 = vec3d.x;
|
||||
double d2 = vec3d.z;
|
||||
double d1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
d1 = vec3d.y + (double)(this.dragon.getRNG().nextFloat() * 20.0F);
|
||||
|
||||
if (d1 >= vec3d.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.targetLocation = new Vec3d(d0, d1, d2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.fireballCharge = 0;
|
||||
this.targetLocation = null;
|
||||
this.currentPath = null;
|
||||
this.attackTarget = null;
|
||||
}
|
||||
|
||||
public void setTarget(EntityLivingBase p_188686_1_)
|
||||
{
|
||||
this.attackTarget = p_188686_1_;
|
||||
int i = this.dragon.initPathPoints();
|
||||
int j = this.dragon.getNearestPpIdx(this.attackTarget.posX, this.attackTarget.posY, this.attackTarget.posZ);
|
||||
int k = MathHelper.floor(this.attackTarget.posX);
|
||||
int l = MathHelper.floor(this.attackTarget.posZ);
|
||||
double d0 = (double)k - this.dragon.posX;
|
||||
double d1 = (double)l - this.dragon.posZ;
|
||||
double d2 = (double)MathHelper.sqrt(d0 * d0 + d1 * d1);
|
||||
double d3 = Math.min(0.4000000059604645D + d2 / 80.0D - 1.0D, 10.0D);
|
||||
int i1 = MathHelper.floor(this.attackTarget.posY + d3);
|
||||
PathPoint pathpoint = new PathPoint(k, i1, l);
|
||||
this.currentPath = this.dragon.findPath(i, j, pathpoint);
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
this.navigateToNextPathNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseStrafePlayer> getType()
|
||||
{
|
||||
return PhaseList.STRAFE_PLAYER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.boss.EntityDragon;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.gen.feature.WorldGenEndPodium;
|
||||
|
||||
public class PhaseTakeoff extends PhaseBase
|
||||
{
|
||||
private boolean firstTick;
|
||||
private Path currentPath;
|
||||
private Vec3d targetLocation;
|
||||
|
||||
public PhaseTakeoff(EntityDragon dragonIn)
|
||||
{
|
||||
super(dragonIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the phase a chance to update its status.
|
||||
* Called by dragon's onLivingUpdate. Only used when !worldObj.isRemote.
|
||||
*/
|
||||
public void doLocalUpdate()
|
||||
{
|
||||
if (!this.firstTick && this.currentPath != null)
|
||||
{
|
||||
BlockPos blockpos = this.dragon.world.getTopSolidOrLiquidBlock(WorldGenEndPodium.END_PODIUM_LOCATION);
|
||||
double d0 = this.dragon.getDistanceSqToCenter(blockpos);
|
||||
|
||||
if (d0 > 100.0D)
|
||||
{
|
||||
this.dragon.getPhaseManager().setPhase(PhaseList.HOLDING_PATTERN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.firstTick = false;
|
||||
this.findNewTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this phase is set to active
|
||||
*/
|
||||
public void initPhase()
|
||||
{
|
||||
this.firstTick = true;
|
||||
this.currentPath = null;
|
||||
this.targetLocation = null;
|
||||
}
|
||||
|
||||
private void findNewTarget()
|
||||
{
|
||||
int i = this.dragon.initPathPoints();
|
||||
Vec3d vec3d = this.dragon.getHeadLookVec(1.0F);
|
||||
int j = this.dragon.getNearestPpIdx(-vec3d.x * 40.0D, 105.0D, -vec3d.z * 40.0D);
|
||||
|
||||
if (this.dragon.getFightManager() != null && this.dragon.getFightManager().getNumAliveCrystals() > 0)
|
||||
{
|
||||
j = j % 12;
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
j += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
j = j - 12;
|
||||
j = j & 7;
|
||||
j = j + 12;
|
||||
}
|
||||
|
||||
this.currentPath = this.dragon.findPath(i, j, (PathPoint)null);
|
||||
|
||||
if (this.currentPath != null)
|
||||
{
|
||||
this.currentPath.incrementPathIndex();
|
||||
this.navigateToNextPathNode();
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToNextPathNode()
|
||||
{
|
||||
Vec3d vec3d = this.currentPath.getCurrentPos();
|
||||
this.currentPath.incrementPathIndex();
|
||||
double d0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
d0 = vec3d.y + (double)(this.dragon.getRNG().nextFloat() * 20.0F);
|
||||
|
||||
if (d0 >= vec3d.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.targetLocation = new Vec3d(vec3d.x, d0, vec3d.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location the dragon is flying toward
|
||||
*/
|
||||
@Nullable
|
||||
public Vec3d getTargetLocation()
|
||||
{
|
||||
return this.targetLocation;
|
||||
}
|
||||
|
||||
public PhaseList<PhaseTakeoff> getType()
|
||||
{
|
||||
return PhaseList.TAKEOFF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.entity.boss.dragon.phase;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.entity.boss;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
Reference in New Issue
Block a user