base mod created
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public abstract class AbstractIllager extends EntityMob
|
||||
{
|
||||
protected static final DataParameter<Byte> AGGRESSIVE = EntityDataManager.<Byte>createKey(AbstractIllager.class, DataSerializers.BYTE);
|
||||
|
||||
public AbstractIllager(World p_i47509_1_)
|
||||
{
|
||||
super(p_i47509_1_);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(AGGRESSIVE, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
protected boolean isAggressive(int mask)
|
||||
{
|
||||
int i = ((Byte)this.dataManager.get(AGGRESSIVE)).byteValue();
|
||||
return (i & mask) != 0;
|
||||
}
|
||||
|
||||
protected void setAggressive(int mask, boolean value)
|
||||
{
|
||||
int i = ((Byte)this.dataManager.get(AGGRESSIVE)).byteValue();
|
||||
|
||||
if (value)
|
||||
{
|
||||
i = i | mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = i & ~mask;
|
||||
}
|
||||
|
||||
this.dataManager.set(AGGRESSIVE, Byte.valueOf((byte)(i & 255)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.ILLAGER;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public AbstractIllager.IllagerArmPose getArmPose()
|
||||
{
|
||||
return AbstractIllager.IllagerArmPose.CROSSED;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static enum IllagerArmPose
|
||||
{
|
||||
CROSSED,
|
||||
ATTACKING,
|
||||
SPELLCASTING,
|
||||
BOW_AND_ARROW;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Calendar;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIAttackRangedBow;
|
||||
import net.minecraft.entity.ai.EntityAIAvoidEntity;
|
||||
import net.minecraft.entity.ai.EntityAIFleeSun;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIRestrictSun;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.item.EntityBoat;
|
||||
import net.minecraft.entity.passive.EntityWolf;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityTippedArrow;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public abstract class AbstractSkeleton extends EntityMob implements IRangedAttackMob
|
||||
{
|
||||
private static final DataParameter<Boolean> SWINGING_ARMS = EntityDataManager.<Boolean>createKey(AbstractSkeleton.class, DataSerializers.BOOLEAN);
|
||||
private final EntityAIAttackRangedBow<AbstractSkeleton> aiArrowAttack = new EntityAIAttackRangedBow<AbstractSkeleton>(this, 1.0D, 20, 15.0F);
|
||||
private final EntityAIAttackMelee aiAttackOnCollide = new EntityAIAttackMelee(this, 1.2D, false)
|
||||
{
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
AbstractSkeleton.this.setSwingingArms(false);
|
||||
}
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
AbstractSkeleton.this.setSwingingArms(true);
|
||||
}
|
||||
};
|
||||
|
||||
public AbstractSkeleton(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.99F);
|
||||
this.setCombatTask();
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIRestrictSun(this));
|
||||
this.tasks.addTask(3, new EntityAIFleeSun(this, 1.0D));
|
||||
this.tasks.addTask(3, new EntityAIAvoidEntity(this, EntityWolf.class, 6.0F, 1.0D, 1.2D));
|
||||
this.tasks.addTask(5, new EntityAIWanderAvoidWater(this, 1.0D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(6, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, true));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(SWINGING_ARMS, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(this.getStepSound(), 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
protected abstract SoundEvent getStepSound();
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.UNDEAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (this.world.isDaytime() && !this.world.isRemote)
|
||||
{
|
||||
float f = this.getBrightness();
|
||||
BlockPos blockpos = this.getRidingEntity() instanceof EntityBoat ? (new BlockPos(this.posX, (double)Math.round(this.posY), this.posZ)).up() : new BlockPos(this.posX, (double)Math.round(this.posY), this.posZ);
|
||||
|
||||
if (f > 0.5F && this.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.world.canSeeSky(blockpos))
|
||||
{
|
||||
boolean flag = true;
|
||||
ItemStack itemstack = this.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
|
||||
|
||||
if (!itemstack.isEmpty())
|
||||
{
|
||||
if (itemstack.isItemStackDamageable())
|
||||
{
|
||||
itemstack.setItemDamage(itemstack.getItemDamage() + this.rand.nextInt(2));
|
||||
|
||||
if (itemstack.getItemDamage() >= itemstack.getMaxDamage())
|
||||
{
|
||||
this.renderBrokenItemStack(itemstack);
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.setFire(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating while riding another entity
|
||||
*/
|
||||
public void updateRidden()
|
||||
{
|
||||
super.updateRidden();
|
||||
|
||||
if (this.getRidingEntity() instanceof EntityCreature)
|
||||
{
|
||||
EntityCreature entitycreature = (EntityCreature)this.getRidingEntity();
|
||||
this.renderYawOffset = entitycreature.renderYawOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
super.setEquipmentBasedOnDifficulty(difficulty);
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
livingdata = super.onInitialSpawn(difficulty, livingdata);
|
||||
this.setEquipmentBasedOnDifficulty(difficulty);
|
||||
this.setEnchantmentBasedOnDifficulty(difficulty);
|
||||
this.setCombatTask();
|
||||
this.setCanPickUpLoot(this.rand.nextFloat() < 0.55F * difficulty.getClampedAdditionalDifficulty());
|
||||
|
||||
if (this.getItemStackFromSlot(EntityEquipmentSlot.HEAD).isEmpty())
|
||||
{
|
||||
Calendar calendar = this.world.getCurrentDate();
|
||||
|
||||
if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN));
|
||||
this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
return livingdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets this entity's combat AI.
|
||||
*/
|
||||
public void setCombatTask()
|
||||
{
|
||||
if (this.world != null && !this.world.isRemote)
|
||||
{
|
||||
this.tasks.removeTask(this.aiAttackOnCollide);
|
||||
this.tasks.removeTask(this.aiArrowAttack);
|
||||
ItemStack itemstack = this.getHeldItemMainhand();
|
||||
|
||||
if (itemstack.getItem() == Items.BOW)
|
||||
{
|
||||
int i = 20;
|
||||
|
||||
if (this.world.getDifficulty() != EnumDifficulty.HARD)
|
||||
{
|
||||
i = 40;
|
||||
}
|
||||
|
||||
this.aiArrowAttack.setAttackCooldown(i);
|
||||
this.tasks.addTask(4, this.aiArrowAttack);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tasks.addTask(4, this.aiAttackOnCollide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack the specified entity using a ranged attack.
|
||||
*/
|
||||
public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor)
|
||||
{
|
||||
EntityArrow entityarrow = this.getArrow(distanceFactor);
|
||||
double d0 = target.posX - this.posX;
|
||||
double d1 = target.getEntityBoundingBox().minY + (double)(target.height / 3.0F) - entityarrow.posY;
|
||||
double d2 = target.posZ - this.posZ;
|
||||
double d3 = (double)MathHelper.sqrt(d0 * d0 + d2 * d2);
|
||||
entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float)(14 - this.world.getDifficulty().getDifficultyId() * 4));
|
||||
this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
|
||||
this.world.spawnEntity(entityarrow);
|
||||
}
|
||||
|
||||
protected EntityArrow getArrow(float p_190726_1_)
|
||||
{
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(this.world, this);
|
||||
entitytippedarrow.setEnchantmentEffectsFromEntity(this, p_190726_1_);
|
||||
return entitytippedarrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.setCombatTask();
|
||||
}
|
||||
|
||||
public void setItemStackToSlot(EntityEquipmentSlot slotIn, ItemStack stack)
|
||||
{
|
||||
super.setItemStackToSlot(slotIn, stack);
|
||||
|
||||
if (!this.world.isRemote && slotIn == EntityEquipmentSlot.MAINHAND)
|
||||
{
|
||||
this.setCombatTask();
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 1.74F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y Offset of this entity.
|
||||
*/
|
||||
public double getYOffset()
|
||||
{
|
||||
return -0.6D;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isSwingingArms()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(SWINGING_ARMS)).booleanValue();
|
||||
}
|
||||
|
||||
public void setSwingingArms(boolean swingingArms)
|
||||
{
|
||||
this.dataManager.set(SWINGING_ARMS, Boolean.valueOf(swingingArms));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntitySmallFireball;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityBlaze extends EntityMob
|
||||
{
|
||||
/** Random offset used in floating behaviour */
|
||||
private float heightOffset = 0.5F;
|
||||
/** ticks until heightOffset is randomized */
|
||||
private int heightOffsetUpdateTime;
|
||||
private static final DataParameter<Byte> ON_FIRE = EntityDataManager.<Byte>createKey(EntityBlaze.class, DataSerializers.BYTE);
|
||||
|
||||
public EntityBlaze(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setPathPriority(PathNodeType.WATER, -1.0F);
|
||||
this.setPathPriority(PathNodeType.LAVA, 8.0F);
|
||||
this.setPathPriority(PathNodeType.DANGER_FIRE, 0.0F);
|
||||
this.setPathPriority(PathNodeType.DAMAGE_FIRE, 0.0F);
|
||||
this.isImmuneToFire = true;
|
||||
this.experienceValue = 10;
|
||||
}
|
||||
|
||||
public static void registerFixesBlaze(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityBlaze.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(4, new EntityBlaze.AIFireballAttack(this));
|
||||
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
|
||||
this.tasks.addTask(7, new EntityAIWanderAvoidWater(this, 1.0D, 0.0F));
|
||||
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.23000000417232513D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(48.0D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(ON_FIRE, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_BLAZE_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_BLAZE_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_BLAZE_DEATH;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getBrightnessForRender()
|
||||
{
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets how bright this entity is.
|
||||
*/
|
||||
public float getBrightness()
|
||||
{
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (!this.onGround && this.motionY < 0.0D)
|
||||
{
|
||||
this.motionY *= 0.6D;
|
||||
}
|
||||
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
if (this.rand.nextInt(24) == 0 && !this.isSilent())
|
||||
{
|
||||
this.world.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, SoundEvents.ENTITY_BLAZE_BURN, this.getSoundCategory(), 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F, false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
if (this.isWet())
|
||||
{
|
||||
this.attackEntityFrom(DamageSource.DROWN, 1.0F);
|
||||
}
|
||||
|
||||
--this.heightOffsetUpdateTime;
|
||||
|
||||
if (this.heightOffsetUpdateTime <= 0)
|
||||
{
|
||||
this.heightOffsetUpdateTime = 100;
|
||||
this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F;
|
||||
}
|
||||
|
||||
EntityLivingBase entitylivingbase = this.getAttackTarget();
|
||||
|
||||
if (entitylivingbase != null && entitylivingbase.posY + (double)entitylivingbase.getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset)
|
||||
{
|
||||
this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D;
|
||||
this.isAirBorne = true;
|
||||
}
|
||||
|
||||
super.updateAITasks();
|
||||
}
|
||||
|
||||
public void fall(float distance, float damageMultiplier)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entity is on fire. Used by render to add the fire effect on rendering.
|
||||
*/
|
||||
public boolean isBurning()
|
||||
{
|
||||
return this.isCharged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_BLAZE;
|
||||
}
|
||||
|
||||
public boolean isCharged()
|
||||
{
|
||||
return (((Byte)this.dataManager.get(ON_FIRE)).byteValue() & 1) != 0;
|
||||
}
|
||||
|
||||
public void setOnFire(boolean onFire)
|
||||
{
|
||||
byte b0 = ((Byte)this.dataManager.get(ON_FIRE)).byteValue();
|
||||
|
||||
if (onFire)
|
||||
{
|
||||
b0 = (byte)(b0 | 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
b0 = (byte)(b0 & -2);
|
||||
}
|
||||
|
||||
this.dataManager.set(ON_FIRE, Byte.valueOf(b0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the light is not too bright where the mob is spawning
|
||||
*/
|
||||
protected boolean isValidLightLevel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static class AIFireballAttack extends EntityAIBase
|
||||
{
|
||||
private final EntityBlaze blaze;
|
||||
private int attackStep;
|
||||
private int attackTime;
|
||||
|
||||
public AIFireballAttack(EntityBlaze blazeIn)
|
||||
{
|
||||
this.blaze = blazeIn;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
|
||||
return entitylivingbase != null && entitylivingbase.isEntityAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.attackStep = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.blaze.setOnFire(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.attackTime;
|
||||
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
|
||||
double d0 = this.blaze.getDistanceSq(entitylivingbase);
|
||||
|
||||
if (d0 < 4.0D)
|
||||
{
|
||||
if (this.attackTime <= 0)
|
||||
{
|
||||
this.attackTime = 20;
|
||||
this.blaze.attackEntityAsMob(entitylivingbase);
|
||||
}
|
||||
|
||||
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
|
||||
}
|
||||
else if (d0 < this.getFollowDistance() * this.getFollowDistance())
|
||||
{
|
||||
double d1 = entitylivingbase.posX - this.blaze.posX;
|
||||
double d2 = entitylivingbase.getEntityBoundingBox().minY + (double)(entitylivingbase.height / 2.0F) - (this.blaze.posY + (double)(this.blaze.height / 2.0F));
|
||||
double d3 = entitylivingbase.posZ - this.blaze.posZ;
|
||||
|
||||
if (this.attackTime <= 0)
|
||||
{
|
||||
++this.attackStep;
|
||||
|
||||
if (this.attackStep == 1)
|
||||
{
|
||||
this.attackTime = 60;
|
||||
this.blaze.setOnFire(true);
|
||||
}
|
||||
else if (this.attackStep <= 4)
|
||||
{
|
||||
this.attackTime = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attackTime = 100;
|
||||
this.attackStep = 0;
|
||||
this.blaze.setOnFire(false);
|
||||
}
|
||||
|
||||
if (this.attackStep > 1)
|
||||
{
|
||||
float f = MathHelper.sqrt(MathHelper.sqrt(d0)) * 0.5F;
|
||||
this.blaze.world.playEvent((EntityPlayer)null, 1018, new BlockPos((int)this.blaze.posX, (int)this.blaze.posY, (int)this.blaze.posZ), 0);
|
||||
|
||||
for (int i = 0; i < 1; ++i)
|
||||
{
|
||||
EntitySmallFireball entitysmallfireball = new EntitySmallFireball(this.blaze.world, this.blaze, d1 + this.blaze.getRNG().nextGaussian() * (double)f, d2, d3 + this.blaze.getRNG().nextGaussian() * (double)f);
|
||||
entitysmallfireball.posY = this.blaze.posY + (double)(this.blaze.height / 2.0F) + 0.5D;
|
||||
this.blaze.world.spawnEntity(entitysmallfireball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.blaze.getNavigator().clearPath();
|
||||
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
|
||||
}
|
||||
|
||||
super.updateTask();
|
||||
}
|
||||
|
||||
private double getFollowDistance()
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.blaze.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
return iattributeinstance == null ? 16.0D : iattributeinstance.getAttributeValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityCaveSpider extends EntitySpider
|
||||
{
|
||||
public EntityCaveSpider(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.7F, 0.5F);
|
||||
}
|
||||
|
||||
public static void registerFixesCaveSpider(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityCaveSpider.class);
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(12.0D);
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
if (super.attackEntityAsMob(entityIn))
|
||||
{
|
||||
if (entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (this.world.getDifficulty() == EnumDifficulty.NORMAL)
|
||||
{
|
||||
i = 7;
|
||||
}
|
||||
else if (this.world.getDifficulty() == EnumDifficulty.HARD)
|
||||
{
|
||||
i = 15;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
((EntityLivingBase)entityIn).addPotionEffect(new PotionEffect(MobEffects.POISON, i * 20, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
return livingdata;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.45F;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_CAVE_SPIDER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityAreaEffectCloud;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIAvoidEntity;
|
||||
import net.minecraft.entity.ai.EntityAICreeperSwell;
|
||||
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.effect.EntityLightningBolt;
|
||||
import net.minecraft.entity.passive.EntityOcelot;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityCreeper extends EntityMob
|
||||
{
|
||||
private static final DataParameter<Integer> STATE = EntityDataManager.<Integer>createKey(EntityCreeper.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Boolean> POWERED = EntityDataManager.<Boolean>createKey(EntityCreeper.class, DataSerializers.BOOLEAN);
|
||||
private static final DataParameter<Boolean> IGNITED = EntityDataManager.<Boolean>createKey(EntityCreeper.class, DataSerializers.BOOLEAN);
|
||||
/**
|
||||
* Time when this creeper was last in an active state (Messed up code here, probably causes creeper animation to go
|
||||
* weird)
|
||||
*/
|
||||
private int lastActiveTime;
|
||||
/** The amount of time since the creeper was close enough to the player to ignite */
|
||||
private int timeSinceIgnited;
|
||||
private int fuseTime = 30;
|
||||
/** Explosion radius for this creeper. */
|
||||
private int explosionRadius = 3;
|
||||
private int droppedSkulls;
|
||||
|
||||
public EntityCreeper(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.7F);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAICreeperSwell(this));
|
||||
this.tasks.addTask(3, new EntityAIAvoidEntity(this, EntityOcelot.class, 6.0F, 1.0D, 1.2D));
|
||||
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false));
|
||||
this.tasks.addTask(5, new EntityAIWanderAvoidWater(this, 0.8D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(6, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum height from where the entity is alowed to jump (used in pathfinder)
|
||||
*/
|
||||
public int getMaxFallHeight()
|
||||
{
|
||||
return this.getAttackTarget() == null ? 3 : 3 + (int)(this.getHealth() - 1.0F);
|
||||
}
|
||||
|
||||
public void fall(float distance, float damageMultiplier)
|
||||
{
|
||||
super.fall(distance, damageMultiplier);
|
||||
this.timeSinceIgnited = (int)((float)this.timeSinceIgnited + distance * 1.5F);
|
||||
|
||||
if (this.timeSinceIgnited > this.fuseTime - 5)
|
||||
{
|
||||
this.timeSinceIgnited = this.fuseTime - 5;
|
||||
}
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(STATE, Integer.valueOf(-1));
|
||||
this.dataManager.register(POWERED, Boolean.valueOf(false));
|
||||
this.dataManager.register(IGNITED, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
public static void registerFixesCreeper(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityCreeper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
|
||||
if (((Boolean)this.dataManager.get(POWERED)).booleanValue())
|
||||
{
|
||||
compound.setBoolean("powered", true);
|
||||
}
|
||||
|
||||
compound.setShort("Fuse", (short)this.fuseTime);
|
||||
compound.setByte("ExplosionRadius", (byte)this.explosionRadius);
|
||||
compound.setBoolean("ignited", this.hasIgnited());
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.dataManager.set(POWERED, Boolean.valueOf(compound.getBoolean("powered")));
|
||||
|
||||
if (compound.hasKey("Fuse", 99))
|
||||
{
|
||||
this.fuseTime = compound.getShort("Fuse");
|
||||
}
|
||||
|
||||
if (compound.hasKey("ExplosionRadius", 99))
|
||||
{
|
||||
this.explosionRadius = compound.getByte("ExplosionRadius");
|
||||
}
|
||||
|
||||
if (compound.getBoolean("ignited"))
|
||||
{
|
||||
this.ignite();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
if (this.isEntityAlive())
|
||||
{
|
||||
this.lastActiveTime = this.timeSinceIgnited;
|
||||
|
||||
if (this.hasIgnited())
|
||||
{
|
||||
this.setCreeperState(1);
|
||||
}
|
||||
|
||||
int i = this.getCreeperState();
|
||||
|
||||
if (i > 0 && this.timeSinceIgnited == 0)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_CREEPER_PRIMED, 1.0F, 0.5F);
|
||||
}
|
||||
|
||||
this.timeSinceIgnited += i;
|
||||
|
||||
if (this.timeSinceIgnited < 0)
|
||||
{
|
||||
this.timeSinceIgnited = 0;
|
||||
}
|
||||
|
||||
if (this.timeSinceIgnited >= this.fuseTime)
|
||||
{
|
||||
this.timeSinceIgnited = this.fuseTime;
|
||||
this.explode();
|
||||
}
|
||||
}
|
||||
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_CREEPER_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_CREEPER_DEATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mob's health reaches 0.
|
||||
*/
|
||||
public void onDeath(DamageSource cause)
|
||||
{
|
||||
super.onDeath(cause);
|
||||
|
||||
if (this.world.getGameRules().getBoolean("doMobLoot"))
|
||||
{
|
||||
if (cause.getTrueSource() instanceof EntitySkeleton)
|
||||
{
|
||||
int i = Item.getIdFromItem(Items.RECORD_13);
|
||||
int j = Item.getIdFromItem(Items.RECORD_WAIT);
|
||||
int k = i + this.rand.nextInt(j - i + 1);
|
||||
this.dropItem(Item.getItemById(k), 1);
|
||||
}
|
||||
else if (cause.getTrueSource() instanceof EntityCreeper && cause.getTrueSource() != this && ((EntityCreeper)cause.getTrueSource()).getPowered() && ((EntityCreeper)cause.getTrueSource()).ableToCauseSkullDrop())
|
||||
{
|
||||
((EntityCreeper)cause.getTrueSource()).incrementDroppedSkulls();
|
||||
this.entityDropItem(new ItemStack(Items.SKULL, 1, 4), 0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the creeper is powered by a lightning bolt.
|
||||
*/
|
||||
public boolean getPowered()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(POWERED)).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Params: (Float)Render tick. Returns the intensity of the creeper's flash when it is ignited.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getCreeperFlashIntensity(float p_70831_1_)
|
||||
{
|
||||
return ((float)this.lastActiveTime + (float)(this.timeSinceIgnited - this.lastActiveTime) * p_70831_1_) / (float)(this.fuseTime - 2);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_CREEPER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of creeper, -1 is idle, 1 is 'in fuse'
|
||||
*/
|
||||
public int getCreeperState()
|
||||
{
|
||||
return ((Integer)this.dataManager.get(STATE)).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of creeper, -1 to idle and 1 to be 'in fuse'
|
||||
*/
|
||||
public void setCreeperState(int state)
|
||||
{
|
||||
this.dataManager.set(STATE, Integer.valueOf(state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a lightning bolt hits the entity.
|
||||
*/
|
||||
public void onStruckByLightning(EntityLightningBolt lightningBolt)
|
||||
{
|
||||
super.onStruckByLightning(lightningBolt);
|
||||
this.dataManager.set(POWERED, Boolean.valueOf(true));
|
||||
}
|
||||
|
||||
protected boolean processInteract(EntityPlayer player, EnumHand hand)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItem(hand);
|
||||
|
||||
if (itemstack.getItem() == Items.FLINT_AND_STEEL)
|
||||
{
|
||||
this.world.playSound(player, this.posX, this.posY, this.posZ, SoundEvents.ITEM_FLINTANDSTEEL_USE, this.getSoundCategory(), 1.0F, this.rand.nextFloat() * 0.4F + 0.8F);
|
||||
player.swingArm(hand);
|
||||
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
this.ignite();
|
||||
itemstack.damageItem(1, player);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.processInteract(player, hand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an explosion as determined by this creeper's power and explosion radius.
|
||||
*/
|
||||
private void explode()
|
||||
{
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
boolean flag = net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this);
|
||||
float f = this.getPowered() ? 2.0F : 1.0F;
|
||||
this.dead = true;
|
||||
this.world.createExplosion(this, this.posX, this.posY, this.posZ, (float)this.explosionRadius * f, flag);
|
||||
this.setDead();
|
||||
this.spawnLingeringCloud();
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnLingeringCloud()
|
||||
{
|
||||
Collection<PotionEffect> collection = this.getActivePotionEffects();
|
||||
|
||||
if (!collection.isEmpty())
|
||||
{
|
||||
EntityAreaEffectCloud entityareaeffectcloud = new EntityAreaEffectCloud(this.world, this.posX, this.posY, this.posZ);
|
||||
entityareaeffectcloud.setRadius(2.5F);
|
||||
entityareaeffectcloud.setRadiusOnUse(-0.5F);
|
||||
entityareaeffectcloud.setWaitTime(10);
|
||||
entityareaeffectcloud.setDuration(entityareaeffectcloud.getDuration() / 2);
|
||||
entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float)entityareaeffectcloud.getDuration());
|
||||
|
||||
for (PotionEffect potioneffect : collection)
|
||||
{
|
||||
entityareaeffectcloud.addEffect(new PotionEffect(potioneffect));
|
||||
}
|
||||
|
||||
this.world.spawnEntity(entityareaeffectcloud);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasIgnited()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(IGNITED)).booleanValue();
|
||||
}
|
||||
|
||||
public void ignite()
|
||||
{
|
||||
this.dataManager.set(IGNITED, Boolean.valueOf(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an entity is able to drop its skull due to being blown up by this creeper.
|
||||
*
|
||||
* Does not test if this creeper is charged; the caller must do that. However, does test the doMobLoot gamerule.
|
||||
*/
|
||||
public boolean ableToCauseSkullDrop()
|
||||
{
|
||||
return this.droppedSkulls < 1 && this.world.getGameRules().getBoolean("doMobLoot");
|
||||
}
|
||||
|
||||
public void incrementDroppedSkulls()
|
||||
{
|
||||
++this.droppedSkulls;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.network.play.server.SPacketChangeGameState;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityElderGuardian extends EntityGuardian
|
||||
{
|
||||
public EntityElderGuardian(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(this.width * 2.35F, this.height * 2.35F);
|
||||
this.enablePersistence();
|
||||
|
||||
if (this.wander != null)
|
||||
{
|
||||
this.wander.setExecutionChance(400);
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.30000001192092896D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(8.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(80.0D);
|
||||
}
|
||||
|
||||
public static void registerFixesElderGuardian(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityElderGuardian.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ELDER_GUARDIAN;
|
||||
}
|
||||
|
||||
public int getAttackDuration()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void setGhost()
|
||||
{
|
||||
this.clientSideSpikesAnimation = 1.0F;
|
||||
this.clientSideSpikesAnimationO = this.clientSideSpikesAnimation;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_ELDER_GUARDIAN_AMBIENT : SoundEvents.ENTITY_ELDERGUARDIAN_AMBIENTLAND;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_ELDER_GUARDIAN_HURT : SoundEvents.ENTITY_ELDER_GUARDIAN_HURT_LAND;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_ELDER_GUARDIAN_DEATH : SoundEvents.ENTITY_ELDER_GUARDIAN_DEATH_LAND;
|
||||
}
|
||||
|
||||
protected SoundEvent getFlopSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ELDER_GUARDIAN_FLOP;
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
super.updateAITasks();
|
||||
int i = 1200;
|
||||
|
||||
if ((this.ticksExisted + this.getEntityId()) % 1200 == 0)
|
||||
{
|
||||
Potion potion = MobEffects.MINING_FATIGUE;
|
||||
List<EntityPlayerMP> list = this.world.<EntityPlayerMP>getPlayers(EntityPlayerMP.class, new Predicate<EntityPlayerMP>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityPlayerMP p_apply_1_)
|
||||
{
|
||||
return EntityElderGuardian.this.getDistanceSq(p_apply_1_) < 2500.0D && p_apply_1_.interactionManager.survivalOrAdventure();
|
||||
}
|
||||
});
|
||||
int j = 2;
|
||||
int k = 6000;
|
||||
int l = 1200;
|
||||
|
||||
for (EntityPlayerMP entityplayermp : list)
|
||||
{
|
||||
if (!entityplayermp.isPotionActive(potion) || entityplayermp.getActivePotionEffect(potion).getAmplifier() < 2 || entityplayermp.getActivePotionEffect(potion).getDuration() < 1200)
|
||||
{
|
||||
entityplayermp.connection.sendPacket(new SPacketChangeGameState(10, 0.0F));
|
||||
entityplayermp.addPotionEffect(new PotionEffect(potion, 6000, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasHome())
|
||||
{
|
||||
this.setHomePosAndDistance(new BlockPos(this), 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
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.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
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.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.PathNodeType;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EntityDamageSourceIndirect;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityEnderman extends EntityMob
|
||||
{
|
||||
private static final UUID ATTACKING_SPEED_BOOST_ID = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0");
|
||||
private static final AttributeModifier ATTACKING_SPEED_BOOST = (new AttributeModifier(ATTACKING_SPEED_BOOST_ID, "Attacking speed boost", 0.15000000596046448D, 0)).setSaved(false);
|
||||
private static final Set<Block> CARRIABLE_BLOCKS = Sets.<Block>newIdentityHashSet();
|
||||
private static final DataParameter<Optional<IBlockState>> CARRIED_BLOCK = EntityDataManager.<Optional<IBlockState>>createKey(EntityEnderman.class, DataSerializers.OPTIONAL_BLOCK_STATE);
|
||||
private static final DataParameter<Boolean> SCREAMING = EntityDataManager.<Boolean>createKey(EntityEnderman.class, DataSerializers.BOOLEAN);
|
||||
private int lastCreepySound;
|
||||
private int targetChangeTime;
|
||||
|
||||
public EntityEnderman(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 2.9F);
|
||||
this.stepHeight = 1.0F;
|
||||
this.setPathPriority(PathNodeType.WATER, -1.0F);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIAttackMelee(this, 1.0D, false));
|
||||
this.tasks.addTask(7, new EntityAIWanderAvoidWater(this, 1.0D, 0.0F));
|
||||
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.tasks.addTask(10, new EntityEnderman.AIPlaceBlock(this));
|
||||
this.tasks.addTask(11, new EntityEnderman.AITakeBlock(this));
|
||||
this.targetTasks.addTask(1, new EntityEnderman.AIFindPlayer(this));
|
||||
this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityEndermite.class, 10, true, false, new Predicate<EntityEndermite>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityEndermite p_apply_1_)
|
||||
{
|
||||
return p_apply_1_.isSpawnedByPlayer();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(40.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.30000001192092896D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(7.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(64.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the active target the Task system uses for tracking
|
||||
*/
|
||||
public void setAttackTarget(@Nullable EntityLivingBase entitylivingbaseIn)
|
||||
{
|
||||
super.setAttackTarget(entitylivingbaseIn);
|
||||
IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
|
||||
|
||||
if (entitylivingbaseIn == null)
|
||||
{
|
||||
this.targetChangeTime = 0;
|
||||
this.dataManager.set(SCREAMING, Boolean.valueOf(false));
|
||||
iattributeinstance.removeModifier(ATTACKING_SPEED_BOOST);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetChangeTime = this.ticksExisted;
|
||||
this.dataManager.set(SCREAMING, Boolean.valueOf(true));
|
||||
|
||||
if (!iattributeinstance.hasModifier(ATTACKING_SPEED_BOOST))
|
||||
{
|
||||
iattributeinstance.applyModifier(ATTACKING_SPEED_BOOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(CARRIED_BLOCK, Optional.absent());
|
||||
this.dataManager.register(SCREAMING, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
public void playEndermanSound()
|
||||
{
|
||||
if (this.ticksExisted >= this.lastCreepySound + 400)
|
||||
{
|
||||
this.lastCreepySound = this.ticksExisted;
|
||||
|
||||
if (!this.isSilent())
|
||||
{
|
||||
this.world.playSound(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ, SoundEvents.ENTITY_ENDERMEN_STARE, this.getSoundCategory(), 2.5F, 1.0F, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
if (SCREAMING.equals(key) && this.isScreaming() && this.world.isRemote)
|
||||
{
|
||||
this.playEndermanSound();
|
||||
}
|
||||
|
||||
super.notifyDataManagerChange(key);
|
||||
}
|
||||
|
||||
public static void registerFixesEnderman(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityEnderman.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
IBlockState iblockstate = this.getHeldBlockState();
|
||||
|
||||
if (iblockstate != null)
|
||||
{
|
||||
compound.setShort("carried", (short)Block.getIdFromBlock(iblockstate.getBlock()));
|
||||
compound.setShort("carriedData", (short)iblockstate.getBlock().getMetaFromState(iblockstate));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
IBlockState iblockstate;
|
||||
|
||||
if (compound.hasKey("carried", 8))
|
||||
{
|
||||
iblockstate = Block.getBlockFromName(compound.getString("carried")).getStateFromMeta(compound.getShort("carriedData") & 65535);
|
||||
}
|
||||
else
|
||||
{
|
||||
iblockstate = Block.getBlockById(compound.getShort("carried")).getStateFromMeta(compound.getShort("carriedData") & 65535);
|
||||
}
|
||||
|
||||
if (iblockstate == null || iblockstate.getBlock() == null || iblockstate.getMaterial() == Material.AIR)
|
||||
{
|
||||
iblockstate = null;
|
||||
}
|
||||
|
||||
this.setHeldBlockState(iblockstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this enderman should be attacking this player
|
||||
*/
|
||||
private boolean shouldAttackPlayer(EntityPlayer player)
|
||||
{
|
||||
ItemStack itemstack = player.inventory.armorInventory.get(3);
|
||||
|
||||
if (itemstack.getItem() == Item.getItemFromBlock(Blocks.PUMPKIN))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3d vec3d = player.getLook(1.0F).normalize();
|
||||
Vec3d vec3d1 = new Vec3d(this.posX - player.posX, this.getEntityBoundingBox().minY + (double)this.getEyeHeight() - (player.posY + (double)player.getEyeHeight()), this.posZ - player.posZ);
|
||||
double d0 = vec3d1.lengthVector();
|
||||
vec3d1 = vec3d1.normalize();
|
||||
double d1 = vec3d.dotProduct(vec3d1);
|
||||
return d1 > 1.0D - 0.025D / d0 ? player.canEntityBeSeen(this) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 2.55F;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.25D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
|
||||
}
|
||||
}
|
||||
|
||||
this.isJumping = false;
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
if (this.isWet())
|
||||
{
|
||||
this.attackEntityFrom(DamageSource.DROWN, 1.0F);
|
||||
}
|
||||
|
||||
if (this.world.isDaytime() && this.ticksExisted >= this.targetChangeTime + 600)
|
||||
{
|
||||
float f = this.getBrightness();
|
||||
|
||||
if (f > 0.5F && this.world.canSeeSky(new BlockPos(this)) && this.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F)
|
||||
{
|
||||
this.setAttackTarget((EntityLivingBase)null);
|
||||
this.teleportRandomly();
|
||||
}
|
||||
}
|
||||
|
||||
super.updateAITasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleport the enderman to a random nearby position
|
||||
*/
|
||||
protected boolean teleportRandomly()
|
||||
{
|
||||
double d0 = this.posX + (this.rand.nextDouble() - 0.5D) * 64.0D;
|
||||
double d1 = this.posY + (double)(this.rand.nextInt(64) - 32);
|
||||
double d2 = this.posZ + (this.rand.nextDouble() - 0.5D) * 64.0D;
|
||||
return this.teleportTo(d0, d1, d2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleport the enderman to another entity
|
||||
*/
|
||||
protected boolean teleportToEntity(Entity p_70816_1_)
|
||||
{
|
||||
Vec3d vec3d = new Vec3d(this.posX - p_70816_1_.posX, this.getEntityBoundingBox().minY + (double)(this.height / 2.0F) - p_70816_1_.posY + (double)p_70816_1_.getEyeHeight(), this.posZ - p_70816_1_.posZ);
|
||||
vec3d = vec3d.normalize();
|
||||
double d0 = 16.0D;
|
||||
double d1 = this.posX + (this.rand.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D;
|
||||
double d2 = this.posY + (double)(this.rand.nextInt(16) - 8) - vec3d.y * 16.0D;
|
||||
double d3 = this.posZ + (this.rand.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D;
|
||||
return this.teleportTo(d1, d2, d3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleport the enderman
|
||||
*/
|
||||
private boolean teleportTo(double x, double y, double z)
|
||||
{
|
||||
net.minecraftforge.event.entity.living.EnderTeleportEvent event = new net.minecraftforge.event.entity.living.EnderTeleportEvent(this, x, y, z, 0);
|
||||
if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) return false;
|
||||
boolean flag = this.attemptTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ());
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.world.playSound((EntityPlayer)null, this.prevPosX, this.prevPosY, this.prevPosZ, SoundEvents.ENTITY_ENDERMEN_TELEPORT, this.getSoundCategory(), 1.0F, 1.0F);
|
||||
this.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return this.isScreaming() ? SoundEvents.ENTITY_ENDERMEN_SCREAM : SoundEvents.ENTITY_ENDERMEN_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ENDERMEN_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ENDERMEN_DEATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the equipment for this entity.
|
||||
*/
|
||||
protected void dropEquipment(boolean wasRecentlyHit, int lootingModifier)
|
||||
{
|
||||
super.dropEquipment(wasRecentlyHit, lootingModifier);
|
||||
IBlockState iblockstate = this.getHeldBlockState();
|
||||
|
||||
if (iblockstate != null)
|
||||
{
|
||||
Item item = Item.getItemFromBlock(iblockstate.getBlock());
|
||||
int i = item.getHasSubtypes() ? iblockstate.getBlock().getMetaFromState(iblockstate) : 0;
|
||||
this.entityDropItem(new ItemStack(item, 1, i), 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ENDERMAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this enderman's held block state
|
||||
*/
|
||||
public void setHeldBlockState(@Nullable IBlockState state)
|
||||
{
|
||||
this.dataManager.set(CARRIED_BLOCK, Optional.fromNullable(state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this enderman's held block state
|
||||
*/
|
||||
@Nullable
|
||||
public IBlockState getHeldBlockState()
|
||||
{
|
||||
return (IBlockState)((Optional)this.dataManager.get(CARRIED_BLOCK)).orNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isEntityInvulnerable(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (source instanceof EntityDamageSourceIndirect)
|
||||
{
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (this.teleportRandomly())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean flag = super.attackEntityFrom(source, amount);
|
||||
|
||||
if (source.isUnblockable() && this.rand.nextInt(10) != 0)
|
||||
{
|
||||
this.teleportRandomly();
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
/*===================================== Forge Start ==============================*/
|
||||
public static void setCarriable(Block block, boolean canCarry)
|
||||
{
|
||||
if (canCarry) CARRIABLE_BLOCKS.add(block);
|
||||
else CARRIABLE_BLOCKS.remove(block);
|
||||
}
|
||||
public static boolean getCarriable(Block block)
|
||||
{
|
||||
return CARRIABLE_BLOCKS.contains(block);
|
||||
}
|
||||
/*===================================== Forge End ==============================*/
|
||||
|
||||
public boolean isScreaming()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(SCREAMING)).booleanValue();
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
CARRIABLE_BLOCKS.add(Blocks.GRASS);
|
||||
CARRIABLE_BLOCKS.add(Blocks.DIRT);
|
||||
CARRIABLE_BLOCKS.add(Blocks.SAND);
|
||||
CARRIABLE_BLOCKS.add(Blocks.GRAVEL);
|
||||
CARRIABLE_BLOCKS.add(Blocks.YELLOW_FLOWER);
|
||||
CARRIABLE_BLOCKS.add(Blocks.RED_FLOWER);
|
||||
CARRIABLE_BLOCKS.add(Blocks.BROWN_MUSHROOM);
|
||||
CARRIABLE_BLOCKS.add(Blocks.RED_MUSHROOM);
|
||||
CARRIABLE_BLOCKS.add(Blocks.TNT);
|
||||
CARRIABLE_BLOCKS.add(Blocks.CACTUS);
|
||||
CARRIABLE_BLOCKS.add(Blocks.CLAY);
|
||||
CARRIABLE_BLOCKS.add(Blocks.PUMPKIN);
|
||||
CARRIABLE_BLOCKS.add(Blocks.MELON_BLOCK);
|
||||
CARRIABLE_BLOCKS.add(Blocks.MYCELIUM);
|
||||
CARRIABLE_BLOCKS.add(Blocks.NETHERRACK);
|
||||
}
|
||||
|
||||
static class AIFindPlayer extends EntityAINearestAttackableTarget<EntityPlayer>
|
||||
{
|
||||
private final EntityEnderman enderman;
|
||||
/** The player */
|
||||
private EntityPlayer player;
|
||||
private int aggroTime;
|
||||
private int teleportTime;
|
||||
|
||||
public AIFindPlayer(EntityEnderman p_i45842_1_)
|
||||
{
|
||||
super(p_i45842_1_, EntityPlayer.class, false);
|
||||
this.enderman = p_i45842_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
double d0 = this.getTargetDistance();
|
||||
this.player = this.enderman.world.getNearestAttackablePlayer(this.enderman.posX, this.enderman.posY, this.enderman.posZ, d0, d0, (Function)null, new Predicate<EntityPlayer>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityPlayer p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ != null && AIFindPlayer.this.enderman.shouldAttackPlayer(p_apply_1_);
|
||||
}
|
||||
});
|
||||
return this.player != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.aggroTime = 5;
|
||||
this.teleportTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.player = null;
|
||||
super.resetTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.player != null)
|
||||
{
|
||||
if (!this.enderman.shouldAttackPlayer(this.player))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.enderman.faceEntity(this.player, 10.0F, 10.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.targetEntity != null && ((EntityPlayer)this.targetEntity).isEntityAlive() ? true : super.shouldContinueExecuting();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.player != null)
|
||||
{
|
||||
if (--this.aggroTime <= 0)
|
||||
{
|
||||
this.targetEntity = this.player;
|
||||
this.player = null;
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.targetEntity != null)
|
||||
{
|
||||
if (this.enderman.shouldAttackPlayer((EntityPlayer)this.targetEntity))
|
||||
{
|
||||
if (((EntityPlayer)this.targetEntity).getDistanceSq(this.enderman) < 16.0D)
|
||||
{
|
||||
this.enderman.teleportRandomly();
|
||||
}
|
||||
|
||||
this.teleportTime = 0;
|
||||
}
|
||||
else if (((EntityPlayer)this.targetEntity).getDistanceSq(this.enderman) > 256.0D && this.teleportTime++ >= 30 && this.enderman.teleportToEntity(this.targetEntity))
|
||||
{
|
||||
this.teleportTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
super.updateTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AIPlaceBlock extends EntityAIBase
|
||||
{
|
||||
private final EntityEnderman enderman;
|
||||
|
||||
public AIPlaceBlock(EntityEnderman p_i45843_1_)
|
||||
{
|
||||
this.enderman = p_i45843_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.enderman.getHeldBlockState() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.enderman.world, this.enderman))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.enderman.getRNG().nextInt(2000) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
Random random = this.enderman.getRNG();
|
||||
World world = this.enderman.world;
|
||||
int i = MathHelper.floor(this.enderman.posX - 1.0D + random.nextDouble() * 2.0D);
|
||||
int j = MathHelper.floor(this.enderman.posY + random.nextDouble() * 2.0D);
|
||||
int k = MathHelper.floor(this.enderman.posZ - 1.0D + random.nextDouble() * 2.0D);
|
||||
BlockPos blockpos = new BlockPos(i, j, k);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
IBlockState iblockstate1 = world.getBlockState(blockpos.down());
|
||||
IBlockState iblockstate2 = this.enderman.getHeldBlockState();
|
||||
|
||||
if (iblockstate2 != null && this.canPlaceBlock(world, blockpos, iblockstate2.getBlock(), iblockstate, iblockstate1))
|
||||
{
|
||||
world.setBlockState(blockpos, iblockstate2, 3);
|
||||
this.enderman.setHeldBlockState((IBlockState)null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canPlaceBlock(World p_188518_1_, BlockPos p_188518_2_, Block p_188518_3_, IBlockState p_188518_4_, IBlockState p_188518_5_)
|
||||
{
|
||||
if (!p_188518_3_.canPlaceBlockAt(p_188518_1_, p_188518_2_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (p_188518_4_.getMaterial() != Material.AIR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (p_188518_5_.getMaterial() == Material.AIR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p_188518_5_.isFullCube();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AITakeBlock extends EntityAIBase
|
||||
{
|
||||
private final EntityEnderman enderman;
|
||||
|
||||
public AITakeBlock(EntityEnderman p_i45841_1_)
|
||||
{
|
||||
this.enderman = p_i45841_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.enderman.getHeldBlockState() != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.enderman.world, this.enderman))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.enderman.getRNG().nextInt(20) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
Random random = this.enderman.getRNG();
|
||||
World world = this.enderman.world;
|
||||
int i = MathHelper.floor(this.enderman.posX - 2.0D + random.nextDouble() * 4.0D);
|
||||
int j = MathHelper.floor(this.enderman.posY + random.nextDouble() * 3.0D);
|
||||
int k = MathHelper.floor(this.enderman.posZ - 2.0D + random.nextDouble() * 4.0D);
|
||||
BlockPos blockpos = new BlockPos(i, j, k);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
Block block = iblockstate.getBlock();
|
||||
RayTraceResult raytraceresult = world.rayTraceBlocks(new Vec3d((double)((float)MathHelper.floor(this.enderman.posX) + 0.5F), (double)((float)j + 0.5F), (double)((float)MathHelper.floor(this.enderman.posZ) + 0.5F)), new Vec3d((double)((float)i + 0.5F), (double)((float)j + 0.5F), (double)((float)k + 0.5F)), false, true, false);
|
||||
boolean flag = raytraceresult != null && raytraceresult.getBlockPos().equals(blockpos);
|
||||
|
||||
if (EntityEnderman.CARRIABLE_BLOCKS.contains(block) && flag)
|
||||
{
|
||||
this.enderman.setHeldBlockState(iblockstate);
|
||||
world.setBlockToAir(blockpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
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.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityEndermite extends EntityMob
|
||||
{
|
||||
private int lifetime;
|
||||
private boolean playerSpawned;
|
||||
|
||||
public EntityEndermite(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.experienceValue = 3;
|
||||
this.setSize(0.4F, 0.3F);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIAttackMelee(this, 1.0D, false));
|
||||
this.tasks.addTask(3, new EntityAIWanderAvoidWater(this, 1.0D));
|
||||
this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.1F;
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(8.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(2.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
|
||||
* prevent them from trampling crops
|
||||
*/
|
||||
protected boolean canTriggerWalking()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ENDERMITE_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ENDERMITE_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ENDERMITE_DEATH;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_ENDERMITE_STEP, 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ENDERMITE;
|
||||
}
|
||||
|
||||
public static void registerFixesEndermite(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityEndermite.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.lifetime = compound.getInteger("Lifetime");
|
||||
this.playerSpawned = compound.getBoolean("PlayerSpawned");
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("Lifetime", this.lifetime);
|
||||
compound.setBoolean("PlayerSpawned", this.playerSpawned);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
this.renderYawOffset = this.rotationYaw;
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render yaw offset
|
||||
*/
|
||||
public void setRenderYawOffset(float offset)
|
||||
{
|
||||
this.rotationYaw = offset;
|
||||
super.setRenderYawOffset(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y Offset of this entity.
|
||||
*/
|
||||
public double getYOffset()
|
||||
{
|
||||
return 0.1D;
|
||||
}
|
||||
|
||||
public boolean isSpawnedByPlayer()
|
||||
{
|
||||
return this.playerSpawned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this mob was spawned by a player or not.
|
||||
*/
|
||||
public void setSpawnedByPlayer(boolean spawnedByPlayer)
|
||||
{
|
||||
this.playerSpawned = spawnedByPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
super.onLivingUpdate();
|
||||
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!this.isNoDespawnRequired())
|
||||
{
|
||||
++this.lifetime;
|
||||
}
|
||||
|
||||
if (this.lifetime >= 2400)
|
||||
{
|
||||
this.setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the light is not too bright where the mob is spawning
|
||||
*/
|
||||
protected boolean isValidLightLevel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
if (super.getCanSpawnHere())
|
||||
{
|
||||
EntityPlayer entityplayer = this.world.getClosestPlayerToEntity(this, 5.0D);
|
||||
return entityplayer == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.ARTHROPOD;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
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.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAvoidEntity;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.passive.EntitySheep;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityEvokerFangs;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityEvoker extends EntitySpellcasterIllager
|
||||
{
|
||||
private EntitySheep wololoTarget;
|
||||
|
||||
public EntityEvoker(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.95F);
|
||||
this.experienceValue = 10;
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
super.initEntityAI();
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(1, new EntityEvoker.AICastingSpell());
|
||||
this.tasks.addTask(2, new EntityAIAvoidEntity(this, EntityPlayer.class, 8.0F, 0.6D, 1.0D));
|
||||
this.tasks.addTask(4, new EntityEvoker.AISummonSpell());
|
||||
this.tasks.addTask(5, new EntityEvoker.AIAttackSpell());
|
||||
this.tasks.addTask(6, new EntityEvoker.AIWololoSpell());
|
||||
this.tasks.addTask(8, new EntityAIWander(this, 0.6D));
|
||||
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F));
|
||||
this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[] {EntityEvoker.class}));
|
||||
this.targetTasks.addTask(2, (new EntityAINearestAttackableTarget(this, EntityPlayer.class, true)).setUnseenMemoryTicks(300));
|
||||
this.targetTasks.addTask(3, (new EntityAINearestAttackableTarget(this, EntityVillager.class, false)).setUnseenMemoryTicks(300));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, false));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(12.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
}
|
||||
|
||||
public static void registerFixesEvoker(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityEvoker.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
}
|
||||
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_EVOCATION_ILLAGER;
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
super.updateAITasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Entity is on the same team as the given Entity.
|
||||
*/
|
||||
public boolean isOnSameTeam(Entity entityIn)
|
||||
{
|
||||
if (entityIn == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (entityIn == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (super.isOnSameTeam(entityIn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (entityIn instanceof EntityVex)
|
||||
{
|
||||
return this.isOnSameTeam(((EntityVex)entityIn).getOwner());
|
||||
}
|
||||
else if (entityIn instanceof EntityLivingBase && ((EntityLivingBase)entityIn).getCreatureAttribute() == EnumCreatureAttribute.ILLAGER)
|
||||
{
|
||||
return this.getTeam() == null && entityIn.getTeam() == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_EVOCATION_ILLAGER_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.EVOCATION_ILLAGER_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_EVOCATION_ILLAGER_HURT;
|
||||
}
|
||||
|
||||
private void setWololoTarget(@Nullable EntitySheep wololoTargetIn)
|
||||
{
|
||||
this.wololoTarget = wololoTargetIn;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private EntitySheep getWololoTarget()
|
||||
{
|
||||
return this.wololoTarget;
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellSound()
|
||||
{
|
||||
return SoundEvents.EVOCATION_ILLAGER_CAST_SPELL;
|
||||
}
|
||||
|
||||
class AIAttackSpell extends EntitySpellcasterIllager.AIUseSpell
|
||||
{
|
||||
private AIAttackSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
protected int getCastingTime()
|
||||
{
|
||||
return 40;
|
||||
}
|
||||
|
||||
protected int getCastingInterval()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
protected void castSpell()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = EntityEvoker.this.getAttackTarget();
|
||||
double d0 = Math.min(entitylivingbase.posY, EntityEvoker.this.posY);
|
||||
double d1 = Math.max(entitylivingbase.posY, EntityEvoker.this.posY) + 1.0D;
|
||||
float f = (float)MathHelper.atan2(entitylivingbase.posZ - EntityEvoker.this.posZ, entitylivingbase.posX - EntityEvoker.this.posX);
|
||||
|
||||
if (EntityEvoker.this.getDistanceSq(entitylivingbase) < 9.0D)
|
||||
{
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
float f1 = f + (float)i * (float)Math.PI * 0.4F;
|
||||
this.spawnFangs(EntityEvoker.this.posX + (double)MathHelper.cos(f1) * 1.5D, EntityEvoker.this.posZ + (double)MathHelper.sin(f1) * 1.5D, d0, d1, f1, 0);
|
||||
}
|
||||
|
||||
for (int k = 0; k < 8; ++k)
|
||||
{
|
||||
float f2 = f + (float)k * (float)Math.PI * 2.0F / 8.0F + ((float)Math.PI * 2F / 5F);
|
||||
this.spawnFangs(EntityEvoker.this.posX + (double)MathHelper.cos(f2) * 2.5D, EntityEvoker.this.posZ + (double)MathHelper.sin(f2) * 2.5D, d0, d1, f2, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int l = 0; l < 16; ++l)
|
||||
{
|
||||
double d2 = 1.25D * (double)(l + 1);
|
||||
int j = 1 * l;
|
||||
this.spawnFangs(EntityEvoker.this.posX + (double)MathHelper.cos(f) * d2, EntityEvoker.this.posZ + (double)MathHelper.sin(f) * d2, d0, d1, f, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnFangs(double p_190876_1_, double p_190876_3_, double p_190876_5_, double p_190876_7_, float p_190876_9_, int p_190876_10_)
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(p_190876_1_, p_190876_7_, p_190876_3_);
|
||||
boolean flag = false;
|
||||
double d0 = 0.0D;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!EntityEvoker.this.world.isBlockNormalCube(blockpos, true) && EntityEvoker.this.world.isBlockNormalCube(blockpos.down(), true))
|
||||
{
|
||||
if (!EntityEvoker.this.world.isAirBlock(blockpos))
|
||||
{
|
||||
IBlockState iblockstate = EntityEvoker.this.world.getBlockState(blockpos);
|
||||
AxisAlignedBB axisalignedbb = iblockstate.getCollisionBoundingBox(EntityEvoker.this.world, blockpos);
|
||||
|
||||
if (axisalignedbb != null)
|
||||
{
|
||||
d0 = axisalignedbb.maxY;
|
||||
}
|
||||
}
|
||||
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
|
||||
blockpos = blockpos.down();
|
||||
|
||||
if (blockpos.getY() < MathHelper.floor(p_190876_5_) - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
EntityEvokerFangs entityevokerfangs = new EntityEvokerFangs(EntityEvoker.this.world, p_190876_1_, (double)blockpos.getY() + d0, p_190876_3_, p_190876_9_, p_190876_10_, EntityEvoker.this);
|
||||
EntityEvoker.this.world.spawnEntity(entityevokerfangs);
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellPrepareSound()
|
||||
{
|
||||
return SoundEvents.EVOCATION_ILLAGER_PREPARE_ATTACK;
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return EntitySpellcasterIllager.SpellType.FANGS;
|
||||
}
|
||||
}
|
||||
|
||||
class AICastingSpell extends EntitySpellcasterIllager.AICastingApell
|
||||
{
|
||||
private AICastingSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (EntityEvoker.this.getAttackTarget() != null)
|
||||
{
|
||||
EntityEvoker.this.getLookHelper().setLookPositionWithEntity(EntityEvoker.this.getAttackTarget(), (float)EntityEvoker.this.getHorizontalFaceSpeed(), (float)EntityEvoker.this.getVerticalFaceSpeed());
|
||||
}
|
||||
else if (EntityEvoker.this.getWololoTarget() != null)
|
||||
{
|
||||
EntityEvoker.this.getLookHelper().setLookPositionWithEntity(EntityEvoker.this.getWololoTarget(), (float)EntityEvoker.this.getHorizontalFaceSpeed(), (float)EntityEvoker.this.getVerticalFaceSpeed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AISummonSpell extends EntitySpellcasterIllager.AIUseSpell
|
||||
{
|
||||
private AISummonSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!super.shouldExecute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = EntityEvoker.this.world.getEntitiesWithinAABB(EntityVex.class, EntityEvoker.this.getEntityBoundingBox().grow(16.0D)).size();
|
||||
return EntityEvoker.this.rand.nextInt(8) + 1 > i;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getCastingTime()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
protected int getCastingInterval()
|
||||
{
|
||||
return 340;
|
||||
}
|
||||
|
||||
protected void castSpell()
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
BlockPos blockpos = (new BlockPos(EntityEvoker.this)).add(-2 + EntityEvoker.this.rand.nextInt(5), 1, -2 + EntityEvoker.this.rand.nextInt(5));
|
||||
EntityVex entityvex = new EntityVex(EntityEvoker.this.world);
|
||||
entityvex.moveToBlockPosAndAngles(blockpos, 0.0F, 0.0F);
|
||||
entityvex.onInitialSpawn(EntityEvoker.this.world.getDifficultyForLocation(blockpos), (IEntityLivingData)null);
|
||||
entityvex.setOwner(EntityEvoker.this);
|
||||
entityvex.setBoundOrigin(blockpos);
|
||||
entityvex.setLimitedLife(20 * (30 + EntityEvoker.this.rand.nextInt(90)));
|
||||
EntityEvoker.this.world.spawnEntity(entityvex);
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellPrepareSound()
|
||||
{
|
||||
return SoundEvents.EVOCATION_ILLAGER_PREPARE_SUMMON;
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return EntitySpellcasterIllager.SpellType.SUMMON_VEX;
|
||||
}
|
||||
}
|
||||
|
||||
public class AIWololoSpell extends EntitySpellcasterIllager.AIUseSpell
|
||||
{
|
||||
final Predicate<EntitySheep> wololoSelector = new Predicate<EntitySheep>()
|
||||
{
|
||||
public boolean apply(EntitySheep p_apply_1_)
|
||||
{
|
||||
return p_apply_1_.getFleeceColor() == EnumDyeColor.BLUE;
|
||||
}
|
||||
};
|
||||
|
||||
public AIWololoSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (EntityEvoker.this.getAttackTarget() != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (EntityEvoker.this.isSpellcasting())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (EntityEvoker.this.ticksExisted < this.spellCooldown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(EntityEvoker.this.world, EntityEvoker.this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EntitySheep> list = EntityEvoker.this.world.<EntitySheep>getEntitiesWithinAABB(EntitySheep.class, EntityEvoker.this.getEntityBoundingBox().grow(16.0D, 4.0D, 16.0D), this.wololoSelector);
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityEvoker.this.setWololoTarget(list.get(EntityEvoker.this.rand.nextInt(list.size())));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return EntityEvoker.this.getWololoTarget() != null && this.spellWarmup > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
EntityEvoker.this.setWololoTarget((EntitySheep)null);
|
||||
}
|
||||
|
||||
protected void castSpell()
|
||||
{
|
||||
EntitySheep entitysheep = EntityEvoker.this.getWololoTarget();
|
||||
|
||||
if (entitysheep != null && entitysheep.isEntityAlive())
|
||||
{
|
||||
entitysheep.setFleeceColor(EnumDyeColor.RED);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getCastWarmupTime()
|
||||
{
|
||||
return 40;
|
||||
}
|
||||
|
||||
protected int getCastingTime()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
protected int getCastingInterval()
|
||||
{
|
||||
return 140;
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellPrepareSound()
|
||||
{
|
||||
return SoundEvents.EVOCATION_ILLAGER_PREPARE_WOLOLO;
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return EntitySpellcasterIllager.SpellType.WOLOLO;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityFlying;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIFindEntityNearestPlayer;
|
||||
import net.minecraft.entity.ai.EntityMoveHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityLargeFireball;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
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.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityGhast extends EntityFlying implements IMob
|
||||
{
|
||||
private static final DataParameter<Boolean> ATTACKING = EntityDataManager.<Boolean>createKey(EntityGhast.class, DataSerializers.BOOLEAN);
|
||||
/** The explosion radius of spawned fireballs. */
|
||||
private int explosionStrength = 1;
|
||||
|
||||
public EntityGhast(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(4.0F, 4.0F);
|
||||
this.isImmuneToFire = true;
|
||||
this.experienceValue = 5;
|
||||
this.moveHelper = new EntityGhast.GhastMoveHelper(this);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(5, new EntityGhast.AIRandomFly(this));
|
||||
this.tasks.addTask(7, new EntityGhast.AILookAround(this));
|
||||
this.tasks.addTask(7, new EntityGhast.AIFireballAttack(this));
|
||||
this.targetTasks.addTask(1, new EntityAIFindEntityNearestPlayer(this));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isAttacking()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(ATTACKING)).booleanValue();
|
||||
}
|
||||
|
||||
public void setAttacking(boolean attacking)
|
||||
{
|
||||
this.dataManager.set(ATTACKING, Boolean.valueOf(attacking));
|
||||
}
|
||||
|
||||
public int getFireballStrength()
|
||||
{
|
||||
return this.explosionStrength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
|
||||
if (!this.world.isRemote && this.world.getDifficulty() == EnumDifficulty.PEACEFUL)
|
||||
{
|
||||
this.setDead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isEntityInvulnerable(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (source.getImmediateSource() instanceof EntityLargeFireball && source.getTrueSource() instanceof EntityPlayer)
|
||||
{
|
||||
super.attackEntityFrom(source, 1000.0F);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.attackEntityFrom(source, amount);
|
||||
}
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(ATTACKING, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(10.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(100.0D);
|
||||
}
|
||||
|
||||
public SoundCategory getSoundCategory()
|
||||
{
|
||||
return SoundCategory.HOSTILE;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_GHAST_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_GHAST_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_GHAST_DEATH;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_GHAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the volume for the sounds this mob makes.
|
||||
*/
|
||||
protected float getSoundVolume()
|
||||
{
|
||||
return 10.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return this.rand.nextInt(20) == 0 && super.getCanSpawnHere() && this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return how many at most can spawn in a chunk at once.
|
||||
*/
|
||||
public int getMaxSpawnedInChunk()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static void registerFixesGhast(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityGhast.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("ExplosionPower", this.explosionStrength);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
|
||||
if (compound.hasKey("ExplosionPower", 99))
|
||||
{
|
||||
this.explosionStrength = compound.getInteger("ExplosionPower");
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 2.6F;
|
||||
}
|
||||
|
||||
static class AIFireballAttack extends EntityAIBase
|
||||
{
|
||||
private final EntityGhast parentEntity;
|
||||
public int attackTimer;
|
||||
|
||||
public AIFireballAttack(EntityGhast ghast)
|
||||
{
|
||||
this.parentEntity = ghast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.parentEntity.getAttackTarget() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.attackTimer = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.parentEntity.setAttacking(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.parentEntity.getAttackTarget();
|
||||
double d0 = 64.0D;
|
||||
|
||||
if (entitylivingbase.getDistanceSq(this.parentEntity) < 4096.0D && this.parentEntity.canEntityBeSeen(entitylivingbase))
|
||||
{
|
||||
World world = this.parentEntity.world;
|
||||
++this.attackTimer;
|
||||
|
||||
if (this.attackTimer == 10)
|
||||
{
|
||||
world.playEvent((EntityPlayer)null, 1015, new BlockPos(this.parentEntity), 0);
|
||||
}
|
||||
|
||||
if (this.attackTimer == 20)
|
||||
{
|
||||
double d1 = 4.0D;
|
||||
Vec3d vec3d = this.parentEntity.getLook(1.0F);
|
||||
double d2 = entitylivingbase.posX - (this.parentEntity.posX + vec3d.x * 4.0D);
|
||||
double d3 = entitylivingbase.getEntityBoundingBox().minY + (double)(entitylivingbase.height / 2.0F) - (0.5D + this.parentEntity.posY + (double)(this.parentEntity.height / 2.0F));
|
||||
double d4 = entitylivingbase.posZ - (this.parentEntity.posZ + vec3d.z * 4.0D);
|
||||
world.playEvent((EntityPlayer)null, 1016, new BlockPos(this.parentEntity), 0);
|
||||
EntityLargeFireball entitylargefireball = new EntityLargeFireball(world, this.parentEntity, d2, d3, d4);
|
||||
entitylargefireball.explosionPower = this.parentEntity.getFireballStrength();
|
||||
entitylargefireball.posX = this.parentEntity.posX + vec3d.x * 4.0D;
|
||||
entitylargefireball.posY = this.parentEntity.posY + (double)(this.parentEntity.height / 2.0F) + 0.5D;
|
||||
entitylargefireball.posZ = this.parentEntity.posZ + vec3d.z * 4.0D;
|
||||
world.spawnEntity(entitylargefireball);
|
||||
this.attackTimer = -40;
|
||||
}
|
||||
}
|
||||
else if (this.attackTimer > 0)
|
||||
{
|
||||
--this.attackTimer;
|
||||
}
|
||||
|
||||
this.parentEntity.setAttacking(this.attackTimer > 10);
|
||||
}
|
||||
}
|
||||
|
||||
static class AILookAround extends EntityAIBase
|
||||
{
|
||||
private final EntityGhast parentEntity;
|
||||
|
||||
public AILookAround(EntityGhast ghast)
|
||||
{
|
||||
this.parentEntity = ghast;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.parentEntity.getAttackTarget() == null)
|
||||
{
|
||||
this.parentEntity.rotationYaw = -((float)MathHelper.atan2(this.parentEntity.motionX, this.parentEntity.motionZ)) * (180F / (float)Math.PI);
|
||||
this.parentEntity.renderYawOffset = this.parentEntity.rotationYaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.parentEntity.getAttackTarget();
|
||||
double d0 = 64.0D;
|
||||
|
||||
if (entitylivingbase.getDistanceSq(this.parentEntity) < 4096.0D)
|
||||
{
|
||||
double d1 = entitylivingbase.posX - this.parentEntity.posX;
|
||||
double d2 = entitylivingbase.posZ - this.parentEntity.posZ;
|
||||
this.parentEntity.rotationYaw = -((float)MathHelper.atan2(d1, d2)) * (180F / (float)Math.PI);
|
||||
this.parentEntity.renderYawOffset = this.parentEntity.rotationYaw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AIRandomFly extends EntityAIBase
|
||||
{
|
||||
private final EntityGhast parentEntity;
|
||||
|
||||
public AIRandomFly(EntityGhast ghast)
|
||||
{
|
||||
this.parentEntity = ghast;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityMoveHelper entitymovehelper = this.parentEntity.getMoveHelper();
|
||||
|
||||
if (!entitymovehelper.isUpdating())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = entitymovehelper.getX() - this.parentEntity.posX;
|
||||
double d1 = entitymovehelper.getY() - this.parentEntity.posY;
|
||||
double d2 = entitymovehelper.getZ() - this.parentEntity.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
return d3 < 1.0D || d3 > 3600.0D;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
Random random = this.parentEntity.getRNG();
|
||||
double d0 = this.parentEntity.posX + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
|
||||
double d1 = this.parentEntity.posY + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
|
||||
double d2 = this.parentEntity.posZ + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
|
||||
this.parentEntity.getMoveHelper().setMoveTo(d0, d1, d2, 1.0D);
|
||||
}
|
||||
}
|
||||
|
||||
static class GhastMoveHelper extends EntityMoveHelper
|
||||
{
|
||||
private final EntityGhast parentEntity;
|
||||
private int courseChangeCooldown;
|
||||
|
||||
public GhastMoveHelper(EntityGhast ghast)
|
||||
{
|
||||
super(ghast);
|
||||
this.parentEntity = ghast;
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
if (this.action == EntityMoveHelper.Action.MOVE_TO)
|
||||
{
|
||||
double d0 = this.posX - this.parentEntity.posX;
|
||||
double d1 = this.posY - this.parentEntity.posY;
|
||||
double d2 = this.posZ - this.parentEntity.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
|
||||
if (this.courseChangeCooldown-- <= 0)
|
||||
{
|
||||
this.courseChangeCooldown += this.parentEntity.getRNG().nextInt(5) + 2;
|
||||
d3 = (double)MathHelper.sqrt(d3);
|
||||
|
||||
if (this.isNotColliding(this.posX, this.posY, this.posZ, d3))
|
||||
{
|
||||
this.parentEntity.motionX += d0 / d3 * 0.1D;
|
||||
this.parentEntity.motionY += d1 / d3 * 0.1D;
|
||||
this.parentEntity.motionZ += d2 / d3 * 0.1D;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity bounding box is not colliding with terrain
|
||||
*/
|
||||
private boolean isNotColliding(double x, double y, double z, double p_179926_7_)
|
||||
{
|
||||
double d0 = (x - this.parentEntity.posX) / p_179926_7_;
|
||||
double d1 = (y - this.parentEntity.posY) / p_179926_7_;
|
||||
double d2 = (z - this.parentEntity.posZ) / p_179926_7_;
|
||||
AxisAlignedBB axisalignedbb = this.parentEntity.getEntityBoundingBox();
|
||||
|
||||
for (int i = 1; (double)i < p_179926_7_; ++i)
|
||||
{
|
||||
axisalignedbb = axisalignedbb.offset(d0, d1, d2);
|
||||
|
||||
if (!this.parentEntity.world.getCollisionBoxes(this.parentEntity, axisalignedbb).isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityGiantZombie extends EntityMob
|
||||
{
|
||||
public EntityGiantZombie(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(this.width * 6.0F, this.height * 6.0F);
|
||||
}
|
||||
|
||||
public static void registerFixesGiantZombie(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityGiantZombie.class);
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 10.440001F;
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(100.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(50.0D);
|
||||
}
|
||||
|
||||
public float getBlockPathWeight(BlockPos pos)
|
||||
{
|
||||
return this.world.getLightBrightness(pos) - 0.5F;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_GIANT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.passive.IAnimals;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class EntityGolem extends EntityCreature implements IAnimals
|
||||
{
|
||||
public EntityGolem(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
public void fall(float distance, float damageMultiplier)
|
||||
{
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of ticks, at least during which the living entity will be silent.
|
||||
*/
|
||||
public int getTalkInterval()
|
||||
{
|
||||
return 120;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an entity can be despawned, used on idle far away entities
|
||||
*/
|
||||
protected boolean canDespawn()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.ai.EntityLookHelper;
|
||||
import net.minecraft.entity.ai.EntityMoveHelper;
|
||||
import net.minecraft.entity.passive.EntitySquid;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNavigateSwimmer;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.util.math.Vec3d;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityGuardian extends EntityMob
|
||||
{
|
||||
private static final DataParameter<Boolean> MOVING = EntityDataManager.<Boolean>createKey(EntityGuardian.class, DataSerializers.BOOLEAN);
|
||||
private static final DataParameter<Integer> TARGET_ENTITY = EntityDataManager.<Integer>createKey(EntityGuardian.class, DataSerializers.VARINT);
|
||||
protected float clientSideTailAnimation;
|
||||
protected float clientSideTailAnimationO;
|
||||
protected float clientSideTailAnimationSpeed;
|
||||
protected float clientSideSpikesAnimation;
|
||||
protected float clientSideSpikesAnimationO;
|
||||
private EntityLivingBase targetedEntity;
|
||||
private int clientSideAttackTime;
|
||||
private boolean clientSideTouchedGround;
|
||||
protected EntityAIWander wander;
|
||||
|
||||
public EntityGuardian(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.experienceValue = 10;
|
||||
this.setSize(0.85F, 0.85F);
|
||||
this.moveHelper = new EntityGuardian.GuardianMoveHelper(this);
|
||||
this.clientSideTailAnimation = this.rand.nextFloat();
|
||||
this.clientSideTailAnimationO = this.clientSideTailAnimation;
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
EntityAIMoveTowardsRestriction entityaimovetowardsrestriction = new EntityAIMoveTowardsRestriction(this, 1.0D);
|
||||
this.wander = new EntityAIWander(this, 1.0D, 80);
|
||||
this.tasks.addTask(4, new EntityGuardian.AIGuardianAttack(this));
|
||||
this.tasks.addTask(5, entityaimovetowardsrestriction);
|
||||
this.tasks.addTask(7, this.wander);
|
||||
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityGuardian.class, 12.0F, 0.01F));
|
||||
this.tasks.addTask(9, new EntityAILookIdle(this));
|
||||
this.wander.setMutexBits(3);
|
||||
entityaimovetowardsrestriction.setMutexBits(3);
|
||||
this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 10, true, false, new EntityGuardian.GuardianTargetSelector(this)));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
|
||||
}
|
||||
|
||||
public static void registerFixesGuardian(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityGuardian.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new PathNavigateGround instance
|
||||
*/
|
||||
protected PathNavigate createNavigator(World worldIn)
|
||||
{
|
||||
return new PathNavigateSwimmer(this, worldIn);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(MOVING, Boolean.valueOf(false));
|
||||
this.dataManager.register(TARGET_ENTITY, Integer.valueOf(0));
|
||||
}
|
||||
|
||||
public boolean isMoving()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(MOVING)).booleanValue();
|
||||
}
|
||||
|
||||
private void setMoving(boolean moving)
|
||||
{
|
||||
this.dataManager.set(MOVING, Boolean.valueOf(moving));
|
||||
}
|
||||
|
||||
public int getAttackDuration()
|
||||
{
|
||||
return 80;
|
||||
}
|
||||
|
||||
private void setTargetedEntity(int entityId)
|
||||
{
|
||||
this.dataManager.set(TARGET_ENTITY, Integer.valueOf(entityId));
|
||||
}
|
||||
|
||||
public boolean hasTargetedEntity()
|
||||
{
|
||||
return ((Integer)this.dataManager.get(TARGET_ENTITY)).intValue() != 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public EntityLivingBase getTargetedEntity()
|
||||
{
|
||||
if (!this.hasTargetedEntity())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (this.world.isRemote)
|
||||
{
|
||||
if (this.targetedEntity != null)
|
||||
{
|
||||
return this.targetedEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity entity = this.world.getEntityByID(((Integer)this.dataManager.get(TARGET_ENTITY)).intValue());
|
||||
|
||||
if (entity instanceof EntityLivingBase)
|
||||
{
|
||||
this.targetedEntity = (EntityLivingBase)entity;
|
||||
return this.targetedEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.getAttackTarget();
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
super.notifyDataManagerChange(key);
|
||||
|
||||
if (TARGET_ENTITY.equals(key))
|
||||
{
|
||||
this.clientSideAttackTime = 0;
|
||||
this.targetedEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of ticks, at least during which the living entity will be silent.
|
||||
*/
|
||||
public int getTalkInterval()
|
||||
{
|
||||
return 160;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_GUARDIAN_AMBIENT : SoundEvents.ENTITY_GUARDIAN_AMBIENT_LAND;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_GUARDIAN_HURT : SoundEvents.ENTITY_GUARDIAN_HURT_LAND;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return this.isInWater() ? SoundEvents.ENTITY_GUARDIAN_DEATH : SoundEvents.ENTITY_GUARDIAN_DEATH_LAND;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
|
||||
* prevent them from trampling crops
|
||||
*/
|
||||
protected boolean canTriggerWalking()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return this.height * 0.5F;
|
||||
}
|
||||
|
||||
public float getBlockPathWeight(BlockPos pos)
|
||||
{
|
||||
return this.world.getBlockState(pos).getMaterial() == Material.WATER ? 10.0F + this.world.getLightBrightness(pos) - 0.5F : super.getBlockPathWeight(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
this.clientSideTailAnimationO = this.clientSideTailAnimation;
|
||||
|
||||
if (!this.isInWater())
|
||||
{
|
||||
this.clientSideTailAnimationSpeed = 2.0F;
|
||||
|
||||
if (this.motionY > 0.0D && this.clientSideTouchedGround && !this.isSilent())
|
||||
{
|
||||
this.world.playSound(this.posX, this.posY, this.posZ, this.getFlopSound(), this.getSoundCategory(), 1.0F, 1.0F, false);
|
||||
}
|
||||
|
||||
this.clientSideTouchedGround = this.motionY < 0.0D && this.world.isBlockNormalCube((new BlockPos(this)).down(), false);
|
||||
}
|
||||
else if (this.isMoving())
|
||||
{
|
||||
if (this.clientSideTailAnimationSpeed < 0.5F)
|
||||
{
|
||||
this.clientSideTailAnimationSpeed = 4.0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clientSideTailAnimationSpeed += (0.5F - this.clientSideTailAnimationSpeed) * 0.1F;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clientSideTailAnimationSpeed += (0.125F - this.clientSideTailAnimationSpeed) * 0.2F;
|
||||
}
|
||||
|
||||
this.clientSideTailAnimation += this.clientSideTailAnimationSpeed;
|
||||
this.clientSideSpikesAnimationO = this.clientSideSpikesAnimation;
|
||||
|
||||
if (!this.isInWater())
|
||||
{
|
||||
this.clientSideSpikesAnimation = this.rand.nextFloat();
|
||||
}
|
||||
else if (this.isMoving())
|
||||
{
|
||||
this.clientSideSpikesAnimation += (0.0F - this.clientSideSpikesAnimation) * 0.25F;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clientSideSpikesAnimation += (1.0F - this.clientSideSpikesAnimation) * 0.06F;
|
||||
}
|
||||
|
||||
if (this.isMoving() && this.isInWater())
|
||||
{
|
||||
Vec3d vec3d = this.getLook(0.0F);
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width - vec3d.x * 1.5D, this.posY + this.rand.nextDouble() * (double)this.height - vec3d.y * 1.5D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width - vec3d.z * 1.5D, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasTargetedEntity())
|
||||
{
|
||||
if (this.clientSideAttackTime < this.getAttackDuration())
|
||||
{
|
||||
++this.clientSideAttackTime;
|
||||
}
|
||||
|
||||
EntityLivingBase entitylivingbase = this.getTargetedEntity();
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
this.getLookHelper().setLookPositionWithEntity(entitylivingbase, 90.0F, 90.0F);
|
||||
this.getLookHelper().onUpdateLook();
|
||||
double d5 = (double)this.getAttackAnimationScale(0.0F);
|
||||
double d0 = entitylivingbase.posX - this.posX;
|
||||
double d1 = entitylivingbase.posY + (double)(entitylivingbase.height * 0.5F) - (this.posY + (double)this.getEyeHeight());
|
||||
double d2 = entitylivingbase.posZ - this.posZ;
|
||||
double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
d0 = d0 / d3;
|
||||
d1 = d1 / d3;
|
||||
d2 = d2 / d3;
|
||||
double d4 = this.rand.nextDouble();
|
||||
|
||||
while (d4 < d3)
|
||||
{
|
||||
d4 += 1.8D - d5 + this.rand.nextDouble() * (1.7D - d5);
|
||||
this.world.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + d0 * d4, this.posY + d1 * d4 + (double)this.getEyeHeight(), this.posZ + d2 * d4, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.inWater)
|
||||
{
|
||||
this.setAir(300);
|
||||
}
|
||||
else if (this.onGround)
|
||||
{
|
||||
this.motionY += 0.5D;
|
||||
this.motionX += (double)((this.rand.nextFloat() * 2.0F - 1.0F) * 0.4F);
|
||||
this.motionZ += (double)((this.rand.nextFloat() * 2.0F - 1.0F) * 0.4F);
|
||||
this.rotationYaw = this.rand.nextFloat() * 360.0F;
|
||||
this.onGround = false;
|
||||
this.isAirBorne = true;
|
||||
}
|
||||
|
||||
if (this.hasTargetedEntity())
|
||||
{
|
||||
this.rotationYaw = this.rotationYawHead;
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
protected SoundEvent getFlopSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_GUARDIAN_FLOP;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getTailAnimation(float p_175471_1_)
|
||||
{
|
||||
return this.clientSideTailAnimationO + (this.clientSideTailAnimation - this.clientSideTailAnimationO) * p_175471_1_;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getSpikesAnimation(float p_175469_1_)
|
||||
{
|
||||
return this.clientSideSpikesAnimationO + (this.clientSideSpikesAnimation - this.clientSideSpikesAnimationO) * p_175469_1_;
|
||||
}
|
||||
|
||||
public float getAttackAnimationScale(float p_175477_1_)
|
||||
{
|
||||
return ((float)this.clientSideAttackTime + p_175477_1_) / (float)this.getAttackDuration();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_GUARDIAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the light is not too bright where the mob is spawning
|
||||
*/
|
||||
protected boolean isValidLightLevel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the entity is not colliding with any blocks / liquids
|
||||
*/
|
||||
public boolean isNotColliding()
|
||||
{
|
||||
return this.world.checkNoEntityCollision(this.getEntityBoundingBox(), this) && this.world.getCollisionBoxes(this, this.getEntityBoundingBox()).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return (this.rand.nextInt(20) == 0 || !this.world.canBlockSeeSky(new BlockPos(this))) && super.getCanSpawnHere();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (!this.isMoving() && !source.isMagicDamage() && source.getImmediateSource() instanceof EntityLivingBase)
|
||||
{
|
||||
EntityLivingBase entitylivingbase = (EntityLivingBase)source.getImmediateSource();
|
||||
|
||||
if (!source.isExplosion())
|
||||
{
|
||||
entitylivingbase.attackEntityFrom(DamageSource.causeThornsDamage(this), 2.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.wander != null)
|
||||
{
|
||||
this.wander.makeUpdate();
|
||||
}
|
||||
|
||||
return super.attackEntityFrom(source, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
|
||||
* use in wolves.
|
||||
*/
|
||||
public int getVerticalFaceSpeed()
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
|
||||
public void travel(float strafe, float vertical, float forward)
|
||||
{
|
||||
if (this.isServerWorld() && this.isInWater())
|
||||
{
|
||||
this.moveRelative(strafe, vertical, forward, 0.1F);
|
||||
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.8999999761581421D;
|
||||
this.motionY *= 0.8999999761581421D;
|
||||
this.motionZ *= 0.8999999761581421D;
|
||||
|
||||
if (!this.isMoving() && this.getAttackTarget() == null)
|
||||
{
|
||||
this.motionY -= 0.005D;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.travel(strafe, vertical, forward);
|
||||
}
|
||||
}
|
||||
|
||||
static class AIGuardianAttack extends EntityAIBase
|
||||
{
|
||||
private final EntityGuardian guardian;
|
||||
private int tickCounter;
|
||||
private final boolean isElder;
|
||||
|
||||
public AIGuardianAttack(EntityGuardian guardian)
|
||||
{
|
||||
this.guardian = guardian;
|
||||
this.isElder = guardian instanceof EntityElderGuardian;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.guardian.getAttackTarget();
|
||||
return entitylivingbase != null && entitylivingbase.isEntityAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return super.shouldContinueExecuting() && (this.isElder || this.guardian.getDistanceSq(this.guardian.getAttackTarget()) > 9.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.tickCounter = -10;
|
||||
this.guardian.getNavigator().clearPath();
|
||||
this.guardian.getLookHelper().setLookPositionWithEntity(this.guardian.getAttackTarget(), 90.0F, 90.0F);
|
||||
this.guardian.isAirBorne = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.guardian.setTargetedEntity(0);
|
||||
this.guardian.setAttackTarget((EntityLivingBase)null);
|
||||
this.guardian.wander.makeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.guardian.getAttackTarget();
|
||||
this.guardian.getNavigator().clearPath();
|
||||
this.guardian.getLookHelper().setLookPositionWithEntity(entitylivingbase, 90.0F, 90.0F);
|
||||
|
||||
if (!this.guardian.canEntityBeSeen(entitylivingbase))
|
||||
{
|
||||
this.guardian.setAttackTarget((EntityLivingBase)null);
|
||||
}
|
||||
else
|
||||
{
|
||||
++this.tickCounter;
|
||||
|
||||
if (this.tickCounter == 0)
|
||||
{
|
||||
this.guardian.setTargetedEntity(this.guardian.getAttackTarget().getEntityId());
|
||||
this.guardian.world.setEntityState(this.guardian, (byte)21);
|
||||
}
|
||||
else if (this.tickCounter >= this.guardian.getAttackDuration())
|
||||
{
|
||||
float f = 1.0F;
|
||||
|
||||
if (this.guardian.world.getDifficulty() == EnumDifficulty.HARD)
|
||||
{
|
||||
f += 2.0F;
|
||||
}
|
||||
|
||||
if (this.isElder)
|
||||
{
|
||||
f += 2.0F;
|
||||
}
|
||||
|
||||
entitylivingbase.attackEntityFrom(DamageSource.causeIndirectMagicDamage(this.guardian, this.guardian), f);
|
||||
entitylivingbase.attackEntityFrom(DamageSource.causeMobDamage(this.guardian), (float)this.guardian.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue());
|
||||
this.guardian.setAttackTarget((EntityLivingBase)null);
|
||||
}
|
||||
|
||||
super.updateTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class GuardianMoveHelper extends EntityMoveHelper
|
||||
{
|
||||
private final EntityGuardian entityGuardian;
|
||||
|
||||
public GuardianMoveHelper(EntityGuardian guardian)
|
||||
{
|
||||
super(guardian);
|
||||
this.entityGuardian = guardian;
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
if (this.action == EntityMoveHelper.Action.MOVE_TO && !this.entityGuardian.getNavigator().noPath())
|
||||
{
|
||||
double d0 = this.posX - this.entityGuardian.posX;
|
||||
double d1 = this.posY - this.entityGuardian.posY;
|
||||
double d2 = this.posZ - this.entityGuardian.posZ;
|
||||
double d3 = (double)MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
d1 = d1 / d3;
|
||||
float f = (float)(MathHelper.atan2(d2, d0) * (180D / Math.PI)) - 90.0F;
|
||||
this.entityGuardian.rotationYaw = this.limitAngle(this.entityGuardian.rotationYaw, f, 90.0F);
|
||||
this.entityGuardian.renderYawOffset = this.entityGuardian.rotationYaw;
|
||||
float f1 = (float)(this.speed * this.entityGuardian.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue());
|
||||
this.entityGuardian.setAIMoveSpeed(this.entityGuardian.getAIMoveSpeed() + (f1 - this.entityGuardian.getAIMoveSpeed()) * 0.125F);
|
||||
double d4 = Math.sin((double)(this.entityGuardian.ticksExisted + this.entityGuardian.getEntityId()) * 0.5D) * 0.05D;
|
||||
double d5 = Math.cos((double)(this.entityGuardian.rotationYaw * 0.017453292F));
|
||||
double d6 = Math.sin((double)(this.entityGuardian.rotationYaw * 0.017453292F));
|
||||
this.entityGuardian.motionX += d4 * d5;
|
||||
this.entityGuardian.motionZ += d4 * d6;
|
||||
d4 = Math.sin((double)(this.entityGuardian.ticksExisted + this.entityGuardian.getEntityId()) * 0.75D) * 0.05D;
|
||||
this.entityGuardian.motionY += d4 * (d6 + d5) * 0.25D;
|
||||
this.entityGuardian.motionY += (double)this.entityGuardian.getAIMoveSpeed() * d1 * 0.1D;
|
||||
EntityLookHelper entitylookhelper = this.entityGuardian.getLookHelper();
|
||||
double d7 = this.entityGuardian.posX + d0 / d3 * 2.0D;
|
||||
double d8 = (double)this.entityGuardian.getEyeHeight() + this.entityGuardian.posY + d1 / d3;
|
||||
double d9 = this.entityGuardian.posZ + d2 / d3 * 2.0D;
|
||||
double d10 = entitylookhelper.getLookPosX();
|
||||
double d11 = entitylookhelper.getLookPosY();
|
||||
double d12 = entitylookhelper.getLookPosZ();
|
||||
|
||||
if (!entitylookhelper.getIsLooking())
|
||||
{
|
||||
d10 = d7;
|
||||
d11 = d8;
|
||||
d12 = d9;
|
||||
}
|
||||
|
||||
this.entityGuardian.getLookHelper().setLookPosition(d10 + (d7 - d10) * 0.125D, d11 + (d8 - d11) * 0.125D, d12 + (d9 - d12) * 0.125D, 10.0F, 40.0F);
|
||||
this.entityGuardian.setMoving(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entityGuardian.setAIMoveSpeed(0.0F);
|
||||
this.entityGuardian.setMoving(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class GuardianTargetSelector implements Predicate<EntityLivingBase>
|
||||
{
|
||||
private final EntityGuardian parentEntity;
|
||||
|
||||
public GuardianTargetSelector(EntityGuardian guardian)
|
||||
{
|
||||
this.parentEntity = guardian;
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable EntityLivingBase p_apply_1_)
|
||||
{
|
||||
return (p_apply_1_ instanceof EntityPlayer || p_apply_1_ instanceof EntitySquid) && p_apply_1_.getDistanceSq(this.parentEntity) > 9.0D;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityHusk extends EntityZombie
|
||||
{
|
||||
public EntityHusk(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
public static void registerFixesHusk(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityHusk.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return super.getCanSpawnHere() && this.world.canSeeSky(new BlockPos(this));
|
||||
}
|
||||
|
||||
protected boolean shouldBurnInDay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HUSK_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_HUSK_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HUSK_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HUSK_STEP;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_HUSK;
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
boolean flag = super.attackEntityAsMob(entityIn);
|
||||
|
||||
if (flag && this.getHeldItemMainhand().isEmpty() && entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
float f = this.world.getDifficultyForLocation(new BlockPos(this)).getAdditionalDifficulty();
|
||||
((EntityLivingBase)entityIn).addPotionEffect(new PotionEffect(MobEffects.HUNGER, 140 * (int)f));
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
protected ItemStack getSkullDrop()
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackRangedBow;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityTippedArrow;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
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.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityIllusionIllager extends EntitySpellcasterIllager implements IRangedAttackMob
|
||||
{
|
||||
private int ghostTime;
|
||||
private final Vec3d[][] renderLocations;
|
||||
|
||||
public EntityIllusionIllager(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.95F);
|
||||
this.experienceValue = 5;
|
||||
this.renderLocations = new Vec3d[2][4];
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
this.renderLocations[0][i] = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||
this.renderLocations[1][i] = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
super.initEntityAI();
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(1, new EntitySpellcasterIllager.AICastingApell());
|
||||
this.tasks.addTask(4, new EntityIllusionIllager.AIMirriorSpell());
|
||||
this.tasks.addTask(5, new EntityIllusionIllager.AIBlindnessSpell());
|
||||
this.tasks.addTask(6, new EntityAIAttackRangedBow(this, 0.5D, 20, 15.0F));
|
||||
this.tasks.addTask(8, new EntityAIWander(this, 0.6D));
|
||||
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F));
|
||||
this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[] {EntityIllusionIllager.class}));
|
||||
this.targetTasks.addTask(2, (new EntityAINearestAttackableTarget(this, EntityPlayer.class, true)).setUnseenMemoryTicks(300));
|
||||
this.targetTasks.addTask(3, (new EntityAINearestAttackableTarget(this, EntityVillager.class, false)).setUnseenMemoryTicks(300));
|
||||
this.targetTasks.addTask(3, (new EntityAINearestAttackableTarget(this, EntityIronGolem.class, false)).setUnseenMemoryTicks(300));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(18.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(32.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
|
||||
return super.onInitialSpawn(difficulty, livingdata);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
}
|
||||
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bounding box of this Entity, adjusted to take auxiliary entities into account (e.g. the tile contained
|
||||
* by a minecart, such as a command block).
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public AxisAlignedBB getRenderBoundingBox()
|
||||
{
|
||||
return this.getEntityBoundingBox().grow(3.0D, 0.0D, 3.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
super.onLivingUpdate();
|
||||
|
||||
if (this.world.isRemote && this.isInvisible())
|
||||
{
|
||||
--this.ghostTime;
|
||||
|
||||
if (this.ghostTime < 0)
|
||||
{
|
||||
this.ghostTime = 0;
|
||||
}
|
||||
|
||||
if (this.hurtTime != 1 && this.ticksExisted % 1200 != 0)
|
||||
{
|
||||
if (this.hurtTime == this.maxHurtTime - 1)
|
||||
{
|
||||
this.ghostTime = 3;
|
||||
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
this.renderLocations[0][k] = this.renderLocations[1][k];
|
||||
this.renderLocations[1][k] = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ghostTime = 3;
|
||||
float f = -6.0F;
|
||||
int i = 13;
|
||||
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
this.renderLocations[0][j] = this.renderLocations[1][j];
|
||||
this.renderLocations[1][j] = new Vec3d((double)(-6.0F + (float)this.rand.nextInt(13)) * 0.5D, (double)Math.max(0, this.rand.nextInt(6) - 4), (double)(-6.0F + (float)this.rand.nextInt(13)) * 0.5D);
|
||||
}
|
||||
|
||||
for (int l = 0; l < 16; ++l)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.CLOUD, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
this.world.playSound(this.posX, this.posY, this.posZ, SoundEvents.ENTITY_ILLAGER_MIRROR_MOVE, this.getSoundCategory(), 1.0F, 1.0F, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public Vec3d[] getRenderLocations(float p_193098_1_)
|
||||
{
|
||||
if (this.ghostTime <= 0)
|
||||
{
|
||||
return this.renderLocations[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = (double)(((float)this.ghostTime - p_193098_1_) / 3.0F);
|
||||
d0 = Math.pow(d0, 0.25D);
|
||||
Vec3d[] avec3d = new Vec3d[4];
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
avec3d[i] = this.renderLocations[1][i].scale(1.0D - d0).add(this.renderLocations[0][i].scale(d0));
|
||||
}
|
||||
|
||||
return avec3d;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Entity is on the same team as the given Entity.
|
||||
*/
|
||||
public boolean isOnSameTeam(Entity entityIn)
|
||||
{
|
||||
if (super.isOnSameTeam(entityIn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (entityIn instanceof EntityLivingBase && ((EntityLivingBase)entityIn).getCreatureAttribute() == EnumCreatureAttribute.ILLAGER)
|
||||
{
|
||||
return this.getTeam() == null && entityIn.getTeam() == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLUSION_ILLAGER_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLAGER_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLUSION_ILLAGER_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLAGER_CAST_SPELL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack the specified entity using a ranged attack.
|
||||
*/
|
||||
public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor)
|
||||
{
|
||||
EntityArrow entityarrow = this.createArrowEntity(distanceFactor);
|
||||
double d0 = target.posX - this.posX;
|
||||
double d1 = target.getEntityBoundingBox().minY + (double)(target.height / 3.0F) - entityarrow.posY;
|
||||
double d2 = target.posZ - this.posZ;
|
||||
double d3 = (double)MathHelper.sqrt(d0 * d0 + d2 * d2);
|
||||
entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float)(14 - this.world.getDifficulty().getDifficultyId() * 4));
|
||||
this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
|
||||
this.world.spawnEntity(entityarrow);
|
||||
}
|
||||
|
||||
protected EntityArrow createArrowEntity(float p_193097_1_)
|
||||
{
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(this.world, this);
|
||||
entitytippedarrow.setEnchantmentEffectsFromEntity(this, p_193097_1_);
|
||||
return entitytippedarrow;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isAggressive()
|
||||
{
|
||||
return this.isAggressive(1);
|
||||
}
|
||||
|
||||
public void setSwingingArms(boolean swingingArms)
|
||||
{
|
||||
this.setAggressive(1, swingingArms);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public AbstractIllager.IllagerArmPose getArmPose()
|
||||
{
|
||||
if (this.isSpellcasting())
|
||||
{
|
||||
return AbstractIllager.IllagerArmPose.SPELLCASTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.isAggressive() ? AbstractIllager.IllagerArmPose.BOW_AND_ARROW : AbstractIllager.IllagerArmPose.CROSSED;
|
||||
}
|
||||
}
|
||||
|
||||
class AIBlindnessSpell extends EntitySpellcasterIllager.AIUseSpell
|
||||
{
|
||||
private int lastTargetId;
|
||||
|
||||
private AIBlindnessSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!super.shouldExecute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (EntityIllusionIllager.this.getAttackTarget() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (EntityIllusionIllager.this.getAttackTarget().getEntityId() == this.lastTargetId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityIllusionIllager.this.world.getDifficultyForLocation(new BlockPos(EntityIllusionIllager.this)).isHarderThan((float)EnumDifficulty.NORMAL.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
this.lastTargetId = EntityIllusionIllager.this.getAttackTarget().getEntityId();
|
||||
}
|
||||
|
||||
protected int getCastingTime()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
protected int getCastingInterval()
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
|
||||
protected void castSpell()
|
||||
{
|
||||
EntityIllusionIllager.this.getAttackTarget().addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 400));
|
||||
}
|
||||
|
||||
protected SoundEvent getSpellPrepareSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLAGER_PREPARE_BLINDNESS;
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return EntitySpellcasterIllager.SpellType.BLINDNESS;
|
||||
}
|
||||
}
|
||||
|
||||
class AIMirriorSpell extends EntitySpellcasterIllager.AIUseSpell
|
||||
{
|
||||
private AIMirriorSpell()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!super.shouldExecute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !EntityIllusionIllager.this.isPotionActive(MobEffects.INVISIBILITY);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getCastingTime()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
protected int getCastingInterval()
|
||||
{
|
||||
return 340;
|
||||
}
|
||||
|
||||
protected void castSpell()
|
||||
{
|
||||
EntityIllusionIllager.this.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, 1200));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getSpellPrepareSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ILLAGER_PREPARE_MIRROR;
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return EntitySpellcasterIllager.SpellType.DISAPPEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
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.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIDefendVillage;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookAtVillager;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
|
||||
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
|
||||
import net.minecraft.entity.ai.EntityAIMoveTowardsTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.village.Village;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityIronGolem extends EntityGolem
|
||||
{
|
||||
protected static final DataParameter<Byte> PLAYER_CREATED = EntityDataManager.<Byte>createKey(EntityIronGolem.class, DataSerializers.BYTE);
|
||||
/** deincrements, and a distance-to-home check is done at 0 */
|
||||
private int homeCheckTimer;
|
||||
@Nullable
|
||||
Village village;
|
||||
private int attackTimer;
|
||||
private int holdRoseTick;
|
||||
|
||||
public EntityIronGolem(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(1.4F, 2.7F);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true));
|
||||
this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F));
|
||||
this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 0.6D, true));
|
||||
this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D));
|
||||
this.tasks.addTask(5, new EntityAILookAtVillager(this));
|
||||
this.tasks.addTask(6, new EntityAIWanderAvoidWater(this, 0.6D));
|
||||
this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIDefendVillage(this));
|
||||
this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityLiving.class, 10, false, true, new Predicate<EntityLiving>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityLiving p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ != null && IMob.VISIBLE_MOB_SELECTOR.apply(p_apply_1_) && !(p_apply_1_ instanceof EntityCreeper);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(PLAYER_CREATED, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
if (--this.homeCheckTimer <= 0)
|
||||
{
|
||||
this.homeCheckTimer = 70 + this.rand.nextInt(50);
|
||||
this.village = this.world.getVillageCollection().getNearestVillage(new BlockPos(this), 32);
|
||||
|
||||
if (this.village == null)
|
||||
{
|
||||
this.detachHome();
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos = this.village.getCenter();
|
||||
this.setHomePosAndDistance(blockpos, (int)((float)this.village.getVillageRadius() * 0.6F));
|
||||
}
|
||||
}
|
||||
|
||||
super.updateAITasks();
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(100.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE).setBaseValue(1.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the entity's air supply when underwater
|
||||
*/
|
||||
protected int decreaseAirSupply(int air)
|
||||
{
|
||||
return air;
|
||||
}
|
||||
|
||||
protected void collideWithEntity(Entity entityIn)
|
||||
{
|
||||
if (entityIn instanceof IMob && !(entityIn instanceof EntityCreeper) && this.getRNG().nextInt(20) == 0)
|
||||
{
|
||||
this.setAttackTarget((EntityLivingBase)entityIn);
|
||||
}
|
||||
|
||||
super.collideWithEntity(entityIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
super.onLivingUpdate();
|
||||
|
||||
if (this.attackTimer > 0)
|
||||
{
|
||||
--this.attackTimer;
|
||||
}
|
||||
|
||||
if (this.holdRoseTick > 0)
|
||||
{
|
||||
--this.holdRoseTick;
|
||||
}
|
||||
|
||||
if (this.motionX * this.motionX + this.motionZ * this.motionZ > 2.500000277905201E-7D && this.rand.nextInt(5) == 0)
|
||||
{
|
||||
int i = MathHelper.floor(this.posX);
|
||||
int j = MathHelper.floor(this.posY - 0.20000000298023224D);
|
||||
int k = MathHelper.floor(this.posZ);
|
||||
IBlockState iblockstate = this.world.getBlockState(new BlockPos(i, j, k));
|
||||
|
||||
if (iblockstate.getMaterial() != Material.AIR)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.BLOCK_CRACK, this.posX + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, this.getEntityBoundingBox().minY + 0.1D, this.posZ + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, 4.0D * ((double)this.rand.nextFloat() - 0.5D), 0.5D, ((double)this.rand.nextFloat() - 0.5D) * 4.0D, Block.getStateId(iblockstate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this entity can attack entities of the specified class.
|
||||
*/
|
||||
public boolean canAttackClass(Class <? extends EntityLivingBase > cls)
|
||||
{
|
||||
if (this.isPlayerCreated() && EntityPlayer.class.isAssignableFrom(cls))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return cls == EntityCreeper.class ? false : super.canAttackClass(cls);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerFixesIronGolem(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityIronGolem.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setBoolean("PlayerCreated", this.isPlayerCreated());
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.setPlayerCreated(compound.getBoolean("PlayerCreated"));
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
this.attackTimer = 10;
|
||||
this.world.setEntityState(this, (byte)4);
|
||||
boolean flag = entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), (float)(7 + this.rand.nextInt(15)));
|
||||
|
||||
if (flag)
|
||||
{
|
||||
entityIn.motionY += 0.4000000059604645D;
|
||||
this.applyEnchantments(this, entityIn);
|
||||
}
|
||||
|
||||
this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link World#setEntityState}
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void handleStatusUpdate(byte id)
|
||||
{
|
||||
if (id == 4)
|
||||
{
|
||||
this.attackTimer = 10;
|
||||
this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F);
|
||||
}
|
||||
else if (id == 11)
|
||||
{
|
||||
this.holdRoseTick = 400;
|
||||
}
|
||||
else if (id == 34)
|
||||
{
|
||||
this.holdRoseTick = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
super.handleStatusUpdate(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Village getVillage()
|
||||
{
|
||||
return this.village;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getAttackTimer()
|
||||
{
|
||||
return this.attackTimer;
|
||||
}
|
||||
|
||||
public void setHoldingRose(boolean p_70851_1_)
|
||||
{
|
||||
if (p_70851_1_)
|
||||
{
|
||||
this.holdRoseTick = 400;
|
||||
this.world.setEntityState(this, (byte)11);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.holdRoseTick = 0;
|
||||
this.world.setEntityState(this, (byte)34);
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_IRONGOLEM_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_IRONGOLEM_DEATH;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_IRONGOLEM_STEP, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_IRON_GOLEM;
|
||||
}
|
||||
|
||||
public int getHoldRoseTick()
|
||||
{
|
||||
return this.holdRoseTick;
|
||||
}
|
||||
|
||||
public boolean isPlayerCreated()
|
||||
{
|
||||
return (((Byte)this.dataManager.get(PLAYER_CREATED)).byteValue() & 1) != 0;
|
||||
}
|
||||
|
||||
public void setPlayerCreated(boolean playerCreated)
|
||||
{
|
||||
byte b0 = ((Byte)this.dataManager.get(PLAYER_CREATED)).byteValue();
|
||||
|
||||
if (playerCreated)
|
||||
{
|
||||
this.dataManager.set(PLAYER_CREATED, Byte.valueOf((byte)(b0 | 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dataManager.set(PLAYER_CREATED, Byte.valueOf((byte)(b0 & -2)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mob's health reaches 0.
|
||||
*/
|
||||
public void onDeath(DamageSource cause)
|
||||
{
|
||||
if (!this.isPlayerCreated() && this.attackingPlayer != null && this.village != null)
|
||||
{
|
||||
this.village.modifyPlayerReputation(this.attackingPlayer.getUniqueID(), -5);
|
||||
}
|
||||
|
||||
super.onDeath(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityMagmaCube extends EntitySlime
|
||||
{
|
||||
public EntityMagmaCube(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.isImmuneToFire = true;
|
||||
}
|
||||
|
||||
public static void registerFixesMagmaCube(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityMagmaCube.class);
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.20000000298023224D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the entity is not colliding with any blocks / liquids
|
||||
*/
|
||||
public boolean isNotColliding()
|
||||
{
|
||||
return this.world.checkNoEntityCollision(this.getEntityBoundingBox(), this) && this.world.getCollisionBoxes(this, this.getEntityBoundingBox()).isEmpty() && !this.world.containsAnyLiquid(this.getEntityBoundingBox());
|
||||
}
|
||||
|
||||
protected void setSlimeSize(int size, boolean resetHealth)
|
||||
{
|
||||
super.setSlimeSize(size, resetHealth);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue((double)(size * 3));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getBrightnessForRender()
|
||||
{
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets how bright this entity is.
|
||||
*/
|
||||
public float getBrightness()
|
||||
{
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
protected EnumParticleTypes getParticleType()
|
||||
{
|
||||
return EnumParticleTypes.FLAME;
|
||||
}
|
||||
|
||||
protected EntitySlime createInstance()
|
||||
{
|
||||
return new EntityMagmaCube(this.world);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return this.isSmallSlime() ? LootTableList.EMPTY : LootTableList.ENTITIES_MAGMA_CUBE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entity is on fire. Used by render to add the fire effect on rendering.
|
||||
*/
|
||||
public boolean isBurning()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of time the slime needs to wait between jumps.
|
||||
*/
|
||||
protected int getJumpDelay()
|
||||
{
|
||||
return super.getJumpDelay() * 4;
|
||||
}
|
||||
|
||||
protected void alterSquishAmount()
|
||||
{
|
||||
this.squishAmount *= 0.9F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this entity to do an upwards motion (jumping).
|
||||
*/
|
||||
protected void jump()
|
||||
{
|
||||
this.motionY = (double)(0.42F + (float)this.getSlimeSize() * 0.1F);
|
||||
this.isAirBorne = true;
|
||||
net.minecraftforge.common.ForgeHooks.onLivingJump(this);
|
||||
}
|
||||
|
||||
protected void handleJumpLava()
|
||||
{
|
||||
this.motionY = (double)(0.22F + (float)this.getSlimeSize() * 0.05F);
|
||||
this.isAirBorne = true;
|
||||
}
|
||||
|
||||
public void fall(float distance, float damageMultiplier)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates weather the slime is able to damage the player (based upon the slime's size)
|
||||
*/
|
||||
protected boolean canDamagePlayer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of damage dealt to the player when "attacked" by the slime.
|
||||
*/
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return super.getAttackStrength() + 2;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_MAGMACUBE_HURT : SoundEvents.ENTITY_MAGMACUBE_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_MAGMACUBE_DEATH : SoundEvents.ENTITY_MAGMACUBE_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getSquishSound()
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_MAGMACUBE_SQUISH : SoundEvents.ENTITY_MAGMACUBE_SQUISH;
|
||||
}
|
||||
|
||||
protected SoundEvent getJumpSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_MAGMACUBE_JUMP;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemAxe;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class EntityMob extends EntityCreature implements IMob
|
||||
{
|
||||
public EntityMob(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.experienceValue = 5;
|
||||
}
|
||||
|
||||
public SoundCategory getSoundCategory()
|
||||
{
|
||||
return SoundCategory.HOSTILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.updateArmSwingProgress();
|
||||
float f = this.getBrightness();
|
||||
|
||||
if (f > 0.5F)
|
||||
{
|
||||
this.idleTime += 2;
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
|
||||
if (!this.world.isRemote && this.world.getDifficulty() == EnumDifficulty.PEACEFUL)
|
||||
{
|
||||
this.setDead();
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getSwimSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HOSTILE_SWIM;
|
||||
}
|
||||
|
||||
protected SoundEvent getSplashSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HOSTILE_SPLASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
return this.isEntityInvulnerable(source) ? false : super.attackEntityFrom(source, amount);
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_HOSTILE_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_HOSTILE_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getFallSound(int heightIn)
|
||||
{
|
||||
return heightIn > 4 ? SoundEvents.ENTITY_HOSTILE_BIG_FALL : SoundEvents.ENTITY_HOSTILE_SMALL_FALL;
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
float f = (float)this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue();
|
||||
int i = 0;
|
||||
|
||||
if (entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
f += EnchantmentHelper.getModifierForCreature(this.getHeldItemMainhand(), ((EntityLivingBase)entityIn).getCreatureAttribute());
|
||||
i += EnchantmentHelper.getKnockbackModifier(this);
|
||||
}
|
||||
|
||||
boolean flag = entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), f);
|
||||
|
||||
if (flag)
|
||||
{
|
||||
if (i > 0 && entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
((EntityLivingBase)entityIn).knockBack(this, (float)i * 0.5F, (double)MathHelper.sin(this.rotationYaw * 0.017453292F), (double)(-MathHelper.cos(this.rotationYaw * 0.017453292F)));
|
||||
this.motionX *= 0.6D;
|
||||
this.motionZ *= 0.6D;
|
||||
}
|
||||
|
||||
int j = EnchantmentHelper.getFireAspectModifier(this);
|
||||
|
||||
if (j > 0)
|
||||
{
|
||||
entityIn.setFire(j * 4);
|
||||
}
|
||||
|
||||
if (entityIn instanceof EntityPlayer)
|
||||
{
|
||||
EntityPlayer entityplayer = (EntityPlayer)entityIn;
|
||||
ItemStack itemstack = this.getHeldItemMainhand();
|
||||
ItemStack itemstack1 = entityplayer.isHandActive() ? entityplayer.getActiveItemStack() : ItemStack.EMPTY;
|
||||
|
||||
if (!itemstack.isEmpty() && !itemstack1.isEmpty() && itemstack.getItem().canDisableShield(itemstack, itemstack1, entityplayer, this) && itemstack1.getItem().isShield(itemstack1, entityplayer))
|
||||
{
|
||||
float f1 = 0.25F + (float)EnchantmentHelper.getEfficiencyModifier(this) * 0.05F;
|
||||
|
||||
if (this.rand.nextFloat() < f1)
|
||||
{
|
||||
entityplayer.getCooldownTracker().setCooldown(itemstack1.getItem(), 100);
|
||||
this.world.setEntityState(entityplayer, (byte)30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.applyEnchantments(this, entityIn);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
public float getBlockPathWeight(BlockPos pos)
|
||||
{
|
||||
return 0.5F - this.world.getLightBrightness(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the light is not too bright where the mob is spawning
|
||||
*/
|
||||
protected boolean isValidLightLevel()
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.posX, this.getEntityBoundingBox().minY, this.posZ);
|
||||
|
||||
if (this.world.getLightFor(EnumSkyBlock.SKY, blockpos) > this.rand.nextInt(32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = this.world.getLightFromNeighbors(blockpos);
|
||||
|
||||
if (this.world.isThundering())
|
||||
{
|
||||
int j = this.world.getSkylightSubtracted();
|
||||
this.world.setSkylightSubtracted(10);
|
||||
i = this.world.getLightFromNeighbors(blockpos);
|
||||
this.world.setSkylightSubtracted(j);
|
||||
}
|
||||
|
||||
return i <= this.rand.nextInt(8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL && this.isValidLightLevel() && super.getCanSpawnHere();
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity won't drop items or experience points if this returns false
|
||||
*/
|
||||
protected boolean canDropLoot()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isPreventingPlayerRest(EntityPlayer playerIn)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityPigZombie extends EntityZombie
|
||||
{
|
||||
private static final UUID ATTACK_SPEED_BOOST_MODIFIER_UUID = UUID.fromString("49455A49-7EC5-45BA-B886-3B90B23A1718");
|
||||
private static final AttributeModifier ATTACK_SPEED_BOOST_MODIFIER = (new AttributeModifier(ATTACK_SPEED_BOOST_MODIFIER_UUID, "Attacking speed boost", 0.05D, 0)).setSaved(false);
|
||||
/** Above zero if this PigZombie is Angry. */
|
||||
private int angerLevel;
|
||||
/** A random delay until this PigZombie next makes a sound. */
|
||||
private int randomSoundDelay;
|
||||
private UUID angerTargetUUID;
|
||||
|
||||
public EntityPigZombie(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.isImmuneToFire = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hint to AI tasks that we were attacked by the passed EntityLivingBase and should retaliate. Is not guaranteed to
|
||||
* change our actual active target (for example if we are currently busy attacking someone else)
|
||||
*/
|
||||
public void setRevengeTarget(@Nullable EntityLivingBase livingBase)
|
||||
{
|
||||
super.setRevengeTarget(livingBase);
|
||||
|
||||
if (livingBase != null)
|
||||
{
|
||||
this.angerTargetUUID = livingBase.getUniqueID();
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyEntityAI()
|
||||
{
|
||||
this.targetTasks.addTask(1, new EntityPigZombie.AIHurtByAggressor(this));
|
||||
this.targetTasks.addTask(2, new EntityPigZombie.AITargetAggressor(this));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.23000000417232513D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(5.0D);
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
|
||||
|
||||
if (this.isAngry())
|
||||
{
|
||||
if (!this.isChild() && !iattributeinstance.hasModifier(ATTACK_SPEED_BOOST_MODIFIER))
|
||||
{
|
||||
iattributeinstance.applyModifier(ATTACK_SPEED_BOOST_MODIFIER);
|
||||
}
|
||||
|
||||
--this.angerLevel;
|
||||
}
|
||||
else if (iattributeinstance.hasModifier(ATTACK_SPEED_BOOST_MODIFIER))
|
||||
{
|
||||
iattributeinstance.removeModifier(ATTACK_SPEED_BOOST_MODIFIER);
|
||||
}
|
||||
|
||||
if (this.randomSoundDelay > 0 && --this.randomSoundDelay == 0)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_ZOMBIE_PIG_ANGRY, this.getSoundVolume() * 2.0F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) * 1.8F);
|
||||
}
|
||||
|
||||
if (this.angerLevel > 0 && this.angerTargetUUID != null && this.getRevengeTarget() == null)
|
||||
{
|
||||
EntityPlayer entityplayer = this.world.getPlayerEntityByUUID(this.angerTargetUUID);
|
||||
this.setRevengeTarget(entityplayer);
|
||||
this.attackingPlayer = entityplayer;
|
||||
this.recentlyHit = this.getRevengeTimer();
|
||||
}
|
||||
|
||||
super.updateAITasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the entity is not colliding with any blocks / liquids
|
||||
*/
|
||||
public boolean isNotColliding()
|
||||
{
|
||||
return this.world.checkNoEntityCollision(this.getEntityBoundingBox(), this) && this.world.getCollisionBoxes(this, this.getEntityBoundingBox()).isEmpty() && !this.world.containsAnyLiquid(this.getEntityBoundingBox());
|
||||
}
|
||||
|
||||
public static void registerFixesPigZombie(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityPigZombie.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setShort("Anger", (short)this.angerLevel);
|
||||
|
||||
if (this.angerTargetUUID != null)
|
||||
{
|
||||
compound.setString("HurtBy", this.angerTargetUUID.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
compound.setString("HurtBy", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.angerLevel = compound.getShort("Anger");
|
||||
String s = compound.getString("HurtBy");
|
||||
|
||||
if (!s.isEmpty())
|
||||
{
|
||||
this.angerTargetUUID = UUID.fromString(s);
|
||||
EntityPlayer entityplayer = this.world.getPlayerEntityByUUID(this.angerTargetUUID);
|
||||
this.setRevengeTarget(entityplayer);
|
||||
|
||||
if (entityplayer != null)
|
||||
{
|
||||
this.attackingPlayer = entityplayer;
|
||||
this.recentlyHit = this.getRevengeTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isEntityInvulnerable(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity entity = source.getTrueSource();
|
||||
|
||||
if (entity instanceof EntityPlayer)
|
||||
{
|
||||
this.becomeAngryAt(entity);
|
||||
}
|
||||
|
||||
return super.attackEntityFrom(source, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this PigZombie to become angry at the supplied Entity (which will be a player).
|
||||
*/
|
||||
private void becomeAngryAt(Entity p_70835_1_)
|
||||
{
|
||||
this.angerLevel = 400 + this.rand.nextInt(400);
|
||||
this.randomSoundDelay = this.rand.nextInt(40);
|
||||
|
||||
if (p_70835_1_ instanceof EntityLivingBase)
|
||||
{
|
||||
this.setRevengeTarget((EntityLivingBase)p_70835_1_);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAngry()
|
||||
{
|
||||
return this.angerLevel > 0;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_PIG_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_PIG_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_PIG_DEATH;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ZOMBIE_PIGMAN;
|
||||
}
|
||||
|
||||
public boolean processInteract(EntityPlayer player, EnumHand hand)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD));
|
||||
}
|
||||
|
||||
protected ItemStack getSkullDrop()
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public boolean isPreventingPlayerRest(EntityPlayer playerIn)
|
||||
{
|
||||
return this.isAngry();
|
||||
}
|
||||
|
||||
static class AIHurtByAggressor extends EntityAIHurtByTarget
|
||||
{
|
||||
public AIHurtByAggressor(EntityPigZombie p_i45828_1_)
|
||||
{
|
||||
super(p_i45828_1_, true);
|
||||
}
|
||||
|
||||
protected void setEntityAttackTarget(EntityCreature creatureIn, EntityLivingBase entityLivingBaseIn)
|
||||
{
|
||||
super.setEntityAttackTarget(creatureIn, entityLivingBaseIn);
|
||||
|
||||
if (creatureIn instanceof EntityPigZombie)
|
||||
{
|
||||
((EntityPigZombie)creatureIn).becomeAngryAt(entityLivingBaseIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AITargetAggressor extends EntityAINearestAttackableTarget<EntityPlayer>
|
||||
{
|
||||
public AITargetAggressor(EntityPigZombie p_i45829_1_)
|
||||
{
|
||||
super(p_i45829_1_, EntityPlayer.class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return ((EntityPigZombie)this.taskOwner).isAngry() && super.shouldExecute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityAgeable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIFollowParent;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIPanic;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.passive.EntityAnimal;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityPolarBear extends EntityAnimal
|
||||
{
|
||||
private static final DataParameter<Boolean> IS_STANDING = EntityDataManager.<Boolean>createKey(EntityPolarBear.class, DataSerializers.BOOLEAN);
|
||||
private float clientSideStandAnimation0;
|
||||
private float clientSideStandAnimation;
|
||||
private int warningSoundTicks;
|
||||
|
||||
public EntityPolarBear(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(1.3F, 1.4F);
|
||||
}
|
||||
|
||||
public EntityAgeable createChild(EntityAgeable ageable)
|
||||
{
|
||||
return new EntityPolarBear(this.world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
|
||||
* the animal type)
|
||||
*/
|
||||
public boolean isBreedingItem(ItemStack stack)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
super.initEntityAI();
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(1, new EntityPolarBear.AIMeleeAttack());
|
||||
this.tasks.addTask(1, new EntityPolarBear.AIPanic());
|
||||
this.tasks.addTask(4, new EntityAIFollowParent(this, 1.25D));
|
||||
this.tasks.addTask(5, new EntityAIWander(this, 1.0D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
|
||||
this.tasks.addTask(7, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityPolarBear.AIHurtByTarget());
|
||||
this.targetTasks.addTask(2, new EntityPolarBear.AIAttackPlayer());
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(20.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return this.isChild() ? SoundEvents.ENTITY_POLAR_BEAR_BABY_AMBIENT : SoundEvents.ENTITY_POLAR_BEAR_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_POLAR_BEAR_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_POLAR_BEAR_DEATH;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_POLAR_BEAR_STEP, 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
protected void playWarningSound()
|
||||
{
|
||||
if (this.warningSoundTicks <= 0)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_POLAR_BEAR_WARNING, 1.0F, 1.0F);
|
||||
this.warningSoundTicks = 40;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_POLAR_BEAR;
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(IS_STANDING, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
this.clientSideStandAnimation0 = this.clientSideStandAnimation;
|
||||
|
||||
if (this.isStanding())
|
||||
{
|
||||
this.clientSideStandAnimation = MathHelper.clamp(this.clientSideStandAnimation + 1.0F, 0.0F, 6.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clientSideStandAnimation = MathHelper.clamp(this.clientSideStandAnimation - 1.0F, 0.0F, 6.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.warningSoundTicks > 0)
|
||||
{
|
||||
--this.warningSoundTicks;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
boolean flag = entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), (float)((int)this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue()));
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.applyEnchantments(this, entityIn);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
public boolean isStanding()
|
||||
{
|
||||
return ((Boolean)this.dataManager.get(IS_STANDING)).booleanValue();
|
||||
}
|
||||
|
||||
public void setStanding(boolean standing)
|
||||
{
|
||||
this.dataManager.set(IS_STANDING, Boolean.valueOf(standing));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getStandingAnimationScale(float p_189795_1_)
|
||||
{
|
||||
return (this.clientSideStandAnimation0 + (this.clientSideStandAnimation - this.clientSideStandAnimation0) * p_189795_1_) / 6.0F;
|
||||
}
|
||||
|
||||
protected float getWaterSlowDown()
|
||||
{
|
||||
return 0.98F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata)
|
||||
{
|
||||
if (livingdata instanceof EntityPolarBear.GroupData)
|
||||
{
|
||||
if (((EntityPolarBear.GroupData)livingdata).madeParent)
|
||||
{
|
||||
this.setGrowingAge(-24000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityPolarBear.GroupData entitypolarbear$groupdata = new EntityPolarBear.GroupData();
|
||||
entitypolarbear$groupdata.madeParent = true;
|
||||
livingdata = entitypolarbear$groupdata;
|
||||
}
|
||||
|
||||
return livingdata;
|
||||
}
|
||||
|
||||
class AIAttackPlayer extends EntityAINearestAttackableTarget<EntityPlayer>
|
||||
{
|
||||
public AIAttackPlayer()
|
||||
{
|
||||
super(EntityPolarBear.this, EntityPlayer.class, 20, true, true, (Predicate)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (EntityPolarBear.this.isChild())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (super.shouldExecute())
|
||||
{
|
||||
for (EntityPolarBear entitypolarbear : EntityPolarBear.this.world.getEntitiesWithinAABB(EntityPolarBear.class, EntityPolarBear.this.getEntityBoundingBox().grow(8.0D, 4.0D, 8.0D)))
|
||||
{
|
||||
if (entitypolarbear.isChild())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityPolarBear.this.setAttackTarget((EntityLivingBase)null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected double getTargetDistance()
|
||||
{
|
||||
return super.getTargetDistance() * 0.5D;
|
||||
}
|
||||
}
|
||||
|
||||
class AIHurtByTarget extends EntityAIHurtByTarget
|
||||
{
|
||||
public AIHurtByTarget()
|
||||
{
|
||||
super(EntityPolarBear.this, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
|
||||
if (EntityPolarBear.this.isChild())
|
||||
{
|
||||
this.alertOthers();
|
||||
this.resetTask();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setEntityAttackTarget(EntityCreature creatureIn, EntityLivingBase entityLivingBaseIn)
|
||||
{
|
||||
if (creatureIn instanceof EntityPolarBear && !creatureIn.isChild())
|
||||
{
|
||||
super.setEntityAttackTarget(creatureIn, entityLivingBaseIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AIMeleeAttack extends EntityAIAttackMelee
|
||||
{
|
||||
public AIMeleeAttack()
|
||||
{
|
||||
super(EntityPolarBear.this, 1.25D, true);
|
||||
}
|
||||
|
||||
protected void checkAndPerformAttack(EntityLivingBase p_190102_1_, double p_190102_2_)
|
||||
{
|
||||
double d0 = this.getAttackReachSqr(p_190102_1_);
|
||||
|
||||
if (p_190102_2_ <= d0 && this.attackTick <= 0)
|
||||
{
|
||||
this.attackTick = 20;
|
||||
this.attacker.attackEntityAsMob(p_190102_1_);
|
||||
EntityPolarBear.this.setStanding(false);
|
||||
}
|
||||
else if (p_190102_2_ <= d0 * 2.0D)
|
||||
{
|
||||
if (this.attackTick <= 0)
|
||||
{
|
||||
EntityPolarBear.this.setStanding(false);
|
||||
this.attackTick = 20;
|
||||
}
|
||||
|
||||
if (this.attackTick <= 10)
|
||||
{
|
||||
EntityPolarBear.this.setStanding(true);
|
||||
EntityPolarBear.this.playWarningSound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attackTick = 20;
|
||||
EntityPolarBear.this.setStanding(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
EntityPolarBear.this.setStanding(false);
|
||||
super.resetTask();
|
||||
}
|
||||
|
||||
protected double getAttackReachSqr(EntityLivingBase attackTarget)
|
||||
{
|
||||
return (double)(4.0F + attackTarget.width);
|
||||
}
|
||||
}
|
||||
|
||||
class AIPanic extends EntityAIPanic
|
||||
{
|
||||
public AIPanic()
|
||||
{
|
||||
super(EntityPolarBear.this, 2.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return !EntityPolarBear.this.isChild() && !EntityPolarBear.this.isBurning() ? false : super.shouldExecute();
|
||||
}
|
||||
}
|
||||
|
||||
static class GroupData implements IEntityLivingData
|
||||
{
|
||||
public boolean madeParent;
|
||||
|
||||
private GroupData()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,903 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.BlockPistonBase;
|
||||
import net.minecraft.block.BlockPistonExtension;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityBodyHelper;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
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.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityShulkerBullet;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityShulker extends EntityGolem implements IMob
|
||||
{
|
||||
private static final UUID COVERED_ARMOR_BONUS_ID = UUID.fromString("7E0292F2-9434-48D5-A29F-9583AF7DF27F");
|
||||
private static final AttributeModifier COVERED_ARMOR_BONUS_MODIFIER = (new AttributeModifier(COVERED_ARMOR_BONUS_ID, "Covered armor bonus", 20.0D, 0)).setSaved(false);
|
||||
protected static final DataParameter<EnumFacing> ATTACHED_FACE = EntityDataManager.<EnumFacing>createKey(EntityShulker.class, DataSerializers.FACING);
|
||||
protected static final DataParameter<Optional<BlockPos>> ATTACHED_BLOCK_POS = EntityDataManager.<Optional<BlockPos>>createKey(EntityShulker.class, DataSerializers.OPTIONAL_BLOCK_POS);
|
||||
protected static final DataParameter<Byte> PEEK_TICK = EntityDataManager.<Byte>createKey(EntityShulker.class, DataSerializers.BYTE);
|
||||
protected static final DataParameter<Byte> COLOR = EntityDataManager.<Byte>createKey(EntityShulker.class, DataSerializers.BYTE);
|
||||
public static final EnumDyeColor DEFAULT_COLOR = EnumDyeColor.PURPLE;
|
||||
private float prevPeekAmount;
|
||||
private float peekAmount;
|
||||
private BlockPos currentAttachmentPosition;
|
||||
private int clientSideTeleportInterpolation;
|
||||
|
||||
public EntityShulker(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(1.0F, 1.0F);
|
||||
this.prevRenderYawOffset = 180.0F;
|
||||
this.renderYawOffset = 180.0F;
|
||||
this.isImmuneToFire = true;
|
||||
this.currentAttachmentPosition = null;
|
||||
this.experienceValue = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
this.renderYawOffset = 180.0F;
|
||||
this.prevRenderYawOffset = 180.0F;
|
||||
this.rotationYaw = 180.0F;
|
||||
this.prevRotationYaw = 180.0F;
|
||||
this.rotationYawHead = 180.0F;
|
||||
this.prevRotationYawHead = 180.0F;
|
||||
return super.onInitialSpawn(difficulty, livingdata);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(4, new EntityShulker.AIAttack());
|
||||
this.tasks.addTask(7, new EntityShulker.AIPeek());
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityShulker.AIAttackNearest(this));
|
||||
this.targetTasks.addTask(3, new EntityShulker.AIDefenseAttack(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
|
||||
* prevent them from trampling crops
|
||||
*/
|
||||
protected boolean canTriggerWalking()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public SoundCategory getSoundCategory()
|
||||
{
|
||||
return SoundCategory.HOSTILE;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SHULKER_AMBIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays living's sound at its position
|
||||
*/
|
||||
public void playLivingSound()
|
||||
{
|
||||
if (!this.isClosed())
|
||||
{
|
||||
super.playLivingSound();
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SHULKER_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return this.isClosed() ? SoundEvents.ENTITY_SHULKER_HURT_CLOSED : SoundEvents.ENTITY_SHULKER_HURT;
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(ATTACHED_FACE, EnumFacing.DOWN);
|
||||
this.dataManager.register(ATTACHED_BLOCK_POS, Optional.absent());
|
||||
this.dataManager.register(PEEK_TICK, Byte.valueOf((byte)0));
|
||||
this.dataManager.register(COLOR, Byte.valueOf((byte)DEFAULT_COLOR.getMetadata()));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
|
||||
}
|
||||
|
||||
protected EntityBodyHelper createBodyHelper()
|
||||
{
|
||||
return new EntityShulker.BodyHelper(this);
|
||||
}
|
||||
|
||||
public static void registerFixesShulker(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityShulker.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.dataManager.set(ATTACHED_FACE, EnumFacing.getFront(compound.getByte("AttachFace")));
|
||||
this.dataManager.set(PEEK_TICK, Byte.valueOf(compound.getByte("Peek")));
|
||||
this.dataManager.set(COLOR, Byte.valueOf(compound.getByte("Color")));
|
||||
|
||||
if (compound.hasKey("APX"))
|
||||
{
|
||||
int i = compound.getInteger("APX");
|
||||
int j = compound.getInteger("APY");
|
||||
int k = compound.getInteger("APZ");
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.of(new BlockPos(i, j, k)));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.absent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setByte("AttachFace", (byte)((EnumFacing)this.dataManager.get(ATTACHED_FACE)).getIndex());
|
||||
compound.setByte("Peek", ((Byte)this.dataManager.get(PEEK_TICK)).byteValue());
|
||||
compound.setByte("Color", ((Byte)this.dataManager.get(COLOR)).byteValue());
|
||||
BlockPos blockpos = this.getAttachmentPos();
|
||||
|
||||
if (blockpos != null)
|
||||
{
|
||||
compound.setInteger("APX", blockpos.getX());
|
||||
compound.setInteger("APY", blockpos.getY());
|
||||
compound.setInteger("APZ", blockpos.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
BlockPos blockpos = (BlockPos)((Optional)this.dataManager.get(ATTACHED_BLOCK_POS)).orNull();
|
||||
|
||||
if (blockpos == null && !this.world.isRemote)
|
||||
{
|
||||
blockpos = new BlockPos(this);
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.of(blockpos));
|
||||
}
|
||||
|
||||
if (this.isRiding())
|
||||
{
|
||||
blockpos = null;
|
||||
float f = this.getRidingEntity().rotationYaw;
|
||||
this.rotationYaw = f;
|
||||
this.renderYawOffset = f;
|
||||
this.prevRenderYawOffset = f;
|
||||
this.clientSideTeleportInterpolation = 0;
|
||||
}
|
||||
else if (!this.world.isRemote)
|
||||
{
|
||||
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
||||
|
||||
if (iblockstate.getMaterial() != Material.AIR)
|
||||
{
|
||||
if (iblockstate.getBlock() == Blocks.PISTON_EXTENSION)
|
||||
{
|
||||
EnumFacing enumfacing = (EnumFacing)iblockstate.getValue(BlockPistonBase.FACING);
|
||||
|
||||
if (this.world.isAirBlock(blockpos.offset(enumfacing)))
|
||||
{
|
||||
blockpos = blockpos.offset(enumfacing);
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.of(blockpos));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
}
|
||||
else if (iblockstate.getBlock() == Blocks.PISTON_HEAD)
|
||||
{
|
||||
EnumFacing enumfacing3 = (EnumFacing)iblockstate.getValue(BlockPistonExtension.FACING);
|
||||
|
||||
if (this.world.isAirBlock(blockpos.offset(enumfacing3)))
|
||||
{
|
||||
blockpos = blockpos.offset(enumfacing3);
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.of(blockpos));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos blockpos1 = blockpos.offset(this.getAttachmentFacing());
|
||||
|
||||
if (!this.world.isBlockNormalCube(blockpos1, false))
|
||||
{
|
||||
boolean flag = false;
|
||||
|
||||
for (EnumFacing enumfacing1 : EnumFacing.values())
|
||||
{
|
||||
blockpos1 = blockpos.offset(enumfacing1);
|
||||
|
||||
if (this.world.isBlockNormalCube(blockpos1, false))
|
||||
{
|
||||
this.dataManager.set(ATTACHED_FACE, enumfacing1);
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos blockpos2 = blockpos.offset(this.getAttachmentFacing().getOpposite());
|
||||
|
||||
if (this.world.isBlockNormalCube(blockpos2, false))
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
}
|
||||
|
||||
float f1 = (float)this.getPeekTick() * 0.01F;
|
||||
this.prevPeekAmount = this.peekAmount;
|
||||
|
||||
if (this.peekAmount > f1)
|
||||
{
|
||||
this.peekAmount = MathHelper.clamp(this.peekAmount - 0.05F, f1, 1.0F);
|
||||
}
|
||||
else if (this.peekAmount < f1)
|
||||
{
|
||||
this.peekAmount = MathHelper.clamp(this.peekAmount + 0.05F, 0.0F, f1);
|
||||
}
|
||||
|
||||
if (blockpos != null)
|
||||
{
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
if (this.clientSideTeleportInterpolation > 0 && this.currentAttachmentPosition != null)
|
||||
{
|
||||
--this.clientSideTeleportInterpolation;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentAttachmentPosition = blockpos;
|
||||
}
|
||||
}
|
||||
|
||||
this.posX = (double)blockpos.getX() + 0.5D;
|
||||
this.posY = (double)blockpos.getY();
|
||||
this.posZ = (double)blockpos.getZ() + 0.5D;
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.lastTickPosX = this.posX;
|
||||
this.lastTickPosY = this.posY;
|
||||
this.lastTickPosZ = this.posZ;
|
||||
double d3 = 0.5D - (double)MathHelper.sin((0.5F + this.peekAmount) * (float)Math.PI) * 0.5D;
|
||||
double d4 = 0.5D - (double)MathHelper.sin((0.5F + this.prevPeekAmount) * (float)Math.PI) * 0.5D;
|
||||
double d5 = d3 - d4;
|
||||
double d0 = 0.0D;
|
||||
double d1 = 0.0D;
|
||||
double d2 = 0.0D;
|
||||
EnumFacing enumfacing2 = this.getAttachmentFacing();
|
||||
|
||||
switch (enumfacing2)
|
||||
{
|
||||
case DOWN:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D, this.posY, this.posZ - 0.5D, this.posX + 0.5D, this.posY + 1.0D + d3, this.posZ + 0.5D));
|
||||
d1 = d5;
|
||||
break;
|
||||
case UP:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D, this.posY - d3, this.posZ - 0.5D, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D));
|
||||
d1 = -d5;
|
||||
break;
|
||||
case NORTH:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D, this.posY, this.posZ - 0.5D, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D + d3));
|
||||
d2 = d5;
|
||||
break;
|
||||
case SOUTH:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D, this.posY, this.posZ - 0.5D - d3, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D));
|
||||
d2 = -d5;
|
||||
break;
|
||||
case WEST:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D, this.posY, this.posZ - 0.5D, this.posX + 0.5D + d3, this.posY + 1.0D, this.posZ + 0.5D));
|
||||
d0 = d5;
|
||||
break;
|
||||
case EAST:
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.5D - d3, this.posY, this.posZ - 0.5D, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D));
|
||||
d0 = -d5;
|
||||
}
|
||||
|
||||
if (d5 > 0.0D)
|
||||
{
|
||||
List<Entity> list = this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox());
|
||||
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
for (Entity entity : list)
|
||||
{
|
||||
if (!(entity instanceof EntityShulker) && !entity.noClip)
|
||||
{
|
||||
entity.move(MoverType.SHULKER, d0, d1, d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to move the entity towards the specified location.
|
||||
*/
|
||||
public void move(MoverType type, double x, double y, double z)
|
||||
{
|
||||
if (type == MoverType.SHULKER_BOX)
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
super.move(type, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x,y,z of the entity from the given parameters. Also seems to set up a bounding box.
|
||||
*/
|
||||
public void setPosition(double x, double y, double z)
|
||||
{
|
||||
super.setPosition(x, y, z);
|
||||
|
||||
if (this.dataManager != null && this.ticksExisted != 0)
|
||||
{
|
||||
Optional<BlockPos> optional = (Optional)this.dataManager.get(ATTACHED_BLOCK_POS);
|
||||
Optional<BlockPos> optional1 = Optional.<BlockPos>of(new BlockPos(x, y, z));
|
||||
|
||||
if (!optional1.equals(optional))
|
||||
{
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, optional1);
|
||||
this.dataManager.set(PEEK_TICK, Byte.valueOf((byte)0));
|
||||
this.isAirBorne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean tryTeleportToNewPosition()
|
||||
{
|
||||
if (!this.isAIDisabled() && this.isEntityAlive())
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.add(8 - this.rand.nextInt(17), 8 - this.rand.nextInt(17), 8 - this.rand.nextInt(17));
|
||||
|
||||
if (blockpos1.getY() > 0 && this.world.isAirBlock(blockpos1) && this.world.isInsideWorldBorder(this) && this.world.getCollisionBoxes(this, new AxisAlignedBB(blockpos1)).isEmpty())
|
||||
{
|
||||
boolean flag = false;
|
||||
|
||||
for (EnumFacing enumfacing : EnumFacing.values())
|
||||
{
|
||||
if (this.world.isBlockNormalCube(blockpos1.offset(enumfacing), false))
|
||||
{
|
||||
this.dataManager.set(ATTACHED_FACE, enumfacing);
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
net.minecraftforge.event.entity.living.EnderTeleportEvent event = new net.minecraftforge.event.entity.living.EnderTeleportEvent(this, blockpos1.getX(), blockpos1.getY(), blockpos1.getZ(), 0);
|
||||
if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) flag = false;
|
||||
blockpos1 = new BlockPos(event.getTargetX(), event.getTargetY(), event.getTargetZ());
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_SHULKER_TELEPORT, 1.0F, 1.0F);
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.of(blockpos1));
|
||||
this.dataManager.set(PEEK_TICK, Byte.valueOf((byte)0));
|
||||
this.setAttackTarget((EntityLivingBase)null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
super.onLivingUpdate();
|
||||
this.motionX = 0.0D;
|
||||
this.motionY = 0.0D;
|
||||
this.motionZ = 0.0D;
|
||||
this.prevRenderYawOffset = 180.0F;
|
||||
this.renderYawOffset = 180.0F;
|
||||
this.rotationYaw = 180.0F;
|
||||
}
|
||||
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
if (ATTACHED_BLOCK_POS.equals(key) && this.world.isRemote && !this.isRiding())
|
||||
{
|
||||
BlockPos blockpos = this.getAttachmentPos();
|
||||
|
||||
if (blockpos != null)
|
||||
{
|
||||
if (this.currentAttachmentPosition == null)
|
||||
{
|
||||
this.currentAttachmentPosition = blockpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.clientSideTeleportInterpolation = 6;
|
||||
}
|
||||
|
||||
this.posX = (double)blockpos.getX() + 0.5D;
|
||||
this.posY = (double)blockpos.getY();
|
||||
this.posZ = (double)blockpos.getZ() + 0.5D;
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.lastTickPosX = this.posX;
|
||||
this.lastTickPosY = this.posY;
|
||||
this.lastTickPosZ = this.posZ;
|
||||
}
|
||||
}
|
||||
|
||||
super.notifyDataManagerChange(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position and rotation values directly without any clamping.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport)
|
||||
{
|
||||
this.newPosRotationIncrements = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isClosed())
|
||||
{
|
||||
Entity entity = source.getImmediateSource();
|
||||
|
||||
if (entity instanceof EntityArrow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (super.attackEntityFrom(source, amount))
|
||||
{
|
||||
if ((double)this.getHealth() < (double)this.getMaxHealth() * 0.5D && this.rand.nextInt(4) == 0)
|
||||
{
|
||||
this.tryTeleportToNewPosition();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isClosed()
|
||||
{
|
||||
return this.getPeekTick() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <b>solid</b> collision bounding box for this entity. Used to make (e.g.) boats solid. Return null if
|
||||
* this entity is not solid.
|
||||
*
|
||||
* For general purposes, use {@link #width} and {@link #height}.
|
||||
*
|
||||
* @see getEntityBoundingBox
|
||||
*/
|
||||
@Nullable
|
||||
public AxisAlignedBB getCollisionBoundingBox()
|
||||
{
|
||||
return this.isEntityAlive() ? this.getEntityBoundingBox() : null;
|
||||
}
|
||||
|
||||
public EnumFacing getAttachmentFacing()
|
||||
{
|
||||
return (EnumFacing)this.dataManager.get(ATTACHED_FACE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPos getAttachmentPos()
|
||||
{
|
||||
return (BlockPos)((Optional)this.dataManager.get(ATTACHED_BLOCK_POS)).orNull();
|
||||
}
|
||||
|
||||
public void setAttachmentPos(@Nullable BlockPos pos)
|
||||
{
|
||||
this.dataManager.set(ATTACHED_BLOCK_POS, Optional.fromNullable(pos));
|
||||
}
|
||||
|
||||
public int getPeekTick()
|
||||
{
|
||||
return ((Byte)this.dataManager.get(PEEK_TICK)).byteValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies or removes armor modifier
|
||||
*/
|
||||
public void updateArmorModifier(int p_184691_1_)
|
||||
{
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).removeModifier(COVERED_ARMOR_BONUS_MODIFIER);
|
||||
|
||||
if (p_184691_1_ == 0)
|
||||
{
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).applyModifier(COVERED_ARMOR_BONUS_MODIFIER);
|
||||
this.playSound(SoundEvents.ENTITY_SHULKER_CLOSE, 1.0F, 1.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_SHULKER_OPEN, 1.0F, 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
this.dataManager.set(PEEK_TICK, Byte.valueOf((byte)p_184691_1_));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getClientPeekAmount(float p_184688_1_)
|
||||
{
|
||||
return this.prevPeekAmount + (this.peekAmount - this.prevPeekAmount) * p_184688_1_;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getClientTeleportInterp()
|
||||
{
|
||||
return this.clientSideTeleportInterpolation;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public BlockPos getOldAttachPos()
|
||||
{
|
||||
return this.currentAttachmentPosition;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.5F;
|
||||
}
|
||||
|
||||
/**
|
||||
* The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
|
||||
* use in wolves.
|
||||
*/
|
||||
public int getVerticalFaceSpeed()
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
|
||||
public int getHorizontalFaceSpeed()
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a velocity to the entities, to push them away from eachother.
|
||||
*/
|
||||
public void applyEntityCollision(Entity entityIn)
|
||||
{
|
||||
}
|
||||
|
||||
public float getCollisionBorderSize()
|
||||
{
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isAttachedToBlock()
|
||||
{
|
||||
return this.currentAttachmentPosition != null && this.getAttachmentPos() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_SHULKER;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public EnumDyeColor getColor()
|
||||
{
|
||||
return EnumDyeColor.byMetadata(((Byte)this.dataManager.get(COLOR)).byteValue());
|
||||
}
|
||||
|
||||
class AIAttack extends EntityAIBase
|
||||
{
|
||||
private int attackTime;
|
||||
|
||||
public AIAttack()
|
||||
{
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = EntityShulker.this.getAttackTarget();
|
||||
|
||||
if (entitylivingbase != null && entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return EntityShulker.this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.attackTime = 20;
|
||||
EntityShulker.this.updateArmorModifier(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
EntityShulker.this.updateArmorModifier(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (EntityShulker.this.world.getDifficulty() != EnumDifficulty.PEACEFUL)
|
||||
{
|
||||
--this.attackTime;
|
||||
EntityLivingBase entitylivingbase = EntityShulker.this.getAttackTarget();
|
||||
EntityShulker.this.getLookHelper().setLookPositionWithEntity(entitylivingbase, 180.0F, 180.0F);
|
||||
double d0 = EntityShulker.this.getDistanceSq(entitylivingbase);
|
||||
|
||||
if (d0 < 400.0D)
|
||||
{
|
||||
if (this.attackTime <= 0)
|
||||
{
|
||||
this.attackTime = 20 + EntityShulker.this.rand.nextInt(10) * 20 / 2;
|
||||
EntityShulkerBullet entityshulkerbullet = new EntityShulkerBullet(EntityShulker.this.world, EntityShulker.this, entitylivingbase, EntityShulker.this.getAttachmentFacing().getAxis());
|
||||
EntityShulker.this.world.spawnEntity(entityshulkerbullet);
|
||||
EntityShulker.this.playSound(SoundEvents.ENTITY_SHULKER_SHOOT, 2.0F, (EntityShulker.this.rand.nextFloat() - EntityShulker.this.rand.nextFloat()) * 0.2F + 1.0F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityShulker.this.setAttackTarget((EntityLivingBase)null);
|
||||
}
|
||||
|
||||
super.updateTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AIAttackNearest extends EntityAINearestAttackableTarget<EntityPlayer>
|
||||
{
|
||||
public AIAttackNearest(EntityShulker shulker)
|
||||
{
|
||||
super(shulker, EntityPlayer.class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return EntityShulker.this.world.getDifficulty() == EnumDifficulty.PEACEFUL ? false : super.shouldExecute();
|
||||
}
|
||||
|
||||
protected AxisAlignedBB getTargetableArea(double targetDistance)
|
||||
{
|
||||
EnumFacing enumfacing = ((EntityShulker)this.taskOwner).getAttachmentFacing();
|
||||
|
||||
if (enumfacing.getAxis() == EnumFacing.Axis.X)
|
||||
{
|
||||
return this.taskOwner.getEntityBoundingBox().grow(4.0D, targetDistance, targetDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
return enumfacing.getAxis() == EnumFacing.Axis.Z ? this.taskOwner.getEntityBoundingBox().grow(targetDistance, targetDistance, 4.0D) : this.taskOwner.getEntityBoundingBox().grow(targetDistance, 4.0D, targetDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AIDefenseAttack extends EntityAINearestAttackableTarget<EntityLivingBase>
|
||||
{
|
||||
public AIDefenseAttack(EntityShulker shulker)
|
||||
{
|
||||
super(shulker, EntityLivingBase.class, 10, true, false, new Predicate<EntityLivingBase>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityLivingBase p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ instanceof IMob;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.taskOwner.getTeam() == null ? false : super.shouldExecute();
|
||||
}
|
||||
|
||||
protected AxisAlignedBB getTargetableArea(double targetDistance)
|
||||
{
|
||||
EnumFacing enumfacing = ((EntityShulker)this.taskOwner).getAttachmentFacing();
|
||||
|
||||
if (enumfacing.getAxis() == EnumFacing.Axis.X)
|
||||
{
|
||||
return this.taskOwner.getEntityBoundingBox().grow(4.0D, targetDistance, targetDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
return enumfacing.getAxis() == EnumFacing.Axis.Z ? this.taskOwner.getEntityBoundingBox().grow(targetDistance, targetDistance, 4.0D) : this.taskOwner.getEntityBoundingBox().grow(targetDistance, 4.0D, targetDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AIPeek extends EntityAIBase
|
||||
{
|
||||
private int peekTime;
|
||||
|
||||
private AIPeek()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return EntityShulker.this.getAttackTarget() == null && EntityShulker.this.rand.nextInt(40) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return EntityShulker.this.getAttackTarget() == null && this.peekTime > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.peekTime = 20 * (1 + EntityShulker.this.rand.nextInt(3));
|
||||
EntityShulker.this.updateArmorModifier(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
if (EntityShulker.this.getAttackTarget() == null)
|
||||
{
|
||||
EntityShulker.this.updateArmorModifier(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.peekTime;
|
||||
}
|
||||
}
|
||||
|
||||
class BodyHelper extends EntityBodyHelper
|
||||
{
|
||||
public BodyHelper(EntityLivingBase theEntity)
|
||||
{
|
||||
super(theEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Head and Body rendenring angles
|
||||
*/
|
||||
public void updateRenderAngles()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockSilverfish;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EntityDamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntitySilverfish extends EntityMob
|
||||
{
|
||||
private EntitySilverfish.AISummonSilverfish summonSilverfish;
|
||||
|
||||
public EntitySilverfish(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.4F, 0.3F);
|
||||
}
|
||||
|
||||
public static void registerFixesSilverfish(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntitySilverfish.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.summonSilverfish = new EntitySilverfish.AISummonSilverfish(this);
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(3, this.summonSilverfish);
|
||||
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false));
|
||||
this.tasks.addTask(5, new EntitySilverfish.AIHideInStone(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y Offset of this entity.
|
||||
*/
|
||||
public double getYOffset()
|
||||
{
|
||||
return 0.1D;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.1F;
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(8.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(1.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
|
||||
* prevent them from trampling crops
|
||||
*/
|
||||
protected boolean canTriggerWalking()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SILVERFISH_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_SILVERFISH_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SILVERFISH_DEATH;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_SILVERFISH_STEP, 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (this.isEntityInvulnerable(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((source instanceof EntityDamageSource || source == DamageSource.MAGIC) && this.summonSilverfish != null)
|
||||
{
|
||||
this.summonSilverfish.notifyHurt();
|
||||
}
|
||||
|
||||
return super.attackEntityFrom(source, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_SILVERFISH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
this.renderYawOffset = this.rotationYaw;
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render yaw offset
|
||||
*/
|
||||
public void setRenderYawOffset(float offset)
|
||||
{
|
||||
this.rotationYaw = offset;
|
||||
super.setRenderYawOffset(offset);
|
||||
}
|
||||
|
||||
public float getBlockPathWeight(BlockPos pos)
|
||||
{
|
||||
return this.world.getBlockState(pos.down()).getBlock() == Blocks.STONE ? 10.0F : super.getBlockPathWeight(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the light is not too bright where the mob is spawning
|
||||
*/
|
||||
protected boolean isValidLightLevel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
if (super.getCanSpawnHere())
|
||||
{
|
||||
EntityPlayer entityplayer = this.world.getNearestPlayerNotCreative(this, 5.0D);
|
||||
return entityplayer == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.ARTHROPOD;
|
||||
}
|
||||
|
||||
static class AIHideInStone extends EntityAIWander
|
||||
{
|
||||
private EnumFacing facing;
|
||||
private boolean doMerge;
|
||||
|
||||
public AIHideInStone(EntitySilverfish silverfishIn)
|
||||
{
|
||||
super(silverfishIn, 1.0D, 10);
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.entity.getAttackTarget() != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.entity.getNavigator().noPath())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Random random = this.entity.getRNG();
|
||||
|
||||
if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.entity.world, this.entity) && random.nextInt(10) == 0)
|
||||
{
|
||||
this.facing = EnumFacing.random(random);
|
||||
BlockPos blockpos = (new BlockPos(this.entity.posX, this.entity.posY + 0.5D, this.entity.posZ)).offset(this.facing);
|
||||
IBlockState iblockstate = this.entity.world.getBlockState(blockpos);
|
||||
|
||||
if (BlockSilverfish.canContainSilverfish(iblockstate))
|
||||
{
|
||||
this.doMerge = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
this.doMerge = false;
|
||||
return super.shouldExecute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.doMerge ? false : super.shouldContinueExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
if (!this.doMerge)
|
||||
{
|
||||
super.startExecuting();
|
||||
}
|
||||
else
|
||||
{
|
||||
World world = this.entity.world;
|
||||
BlockPos blockpos = (new BlockPos(this.entity.posX, this.entity.posY + 0.5D, this.entity.posZ)).offset(this.facing);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
|
||||
if (BlockSilverfish.canContainSilverfish(iblockstate))
|
||||
{
|
||||
world.setBlockState(blockpos, Blocks.MONSTER_EGG.getDefaultState().withProperty(BlockSilverfish.VARIANT, BlockSilverfish.EnumType.forModelBlock(iblockstate)), 3);
|
||||
this.entity.spawnExplosionParticle();
|
||||
this.entity.setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AISummonSilverfish extends EntityAIBase
|
||||
{
|
||||
private final EntitySilverfish silverfish;
|
||||
private int lookForFriends;
|
||||
|
||||
public AISummonSilverfish(EntitySilverfish silverfishIn)
|
||||
{
|
||||
this.silverfish = silverfishIn;
|
||||
}
|
||||
|
||||
public void notifyHurt()
|
||||
{
|
||||
if (this.lookForFriends == 0)
|
||||
{
|
||||
this.lookForFriends = 20;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.lookForFriends > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.lookForFriends;
|
||||
|
||||
if (this.lookForFriends <= 0)
|
||||
{
|
||||
World world = this.silverfish.world;
|
||||
Random random = this.silverfish.getRNG();
|
||||
BlockPos blockpos = new BlockPos(this.silverfish);
|
||||
|
||||
for (int i = 0; i <= 5 && i >= -5; i = (i <= 0 ? 1 : 0) - i)
|
||||
{
|
||||
for (int j = 0; j <= 10 && j >= -10; j = (j <= 0 ? 1 : 0) - j)
|
||||
{
|
||||
for (int k = 0; k <= 10 && k >= -10; k = (k <= 0 ? 1 : 0) - k)
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.add(j, i, k);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos1);
|
||||
|
||||
if (iblockstate.getBlock() == Blocks.MONSTER_EGG)
|
||||
{
|
||||
if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(world, this.silverfish))
|
||||
{
|
||||
world.destroyBlock(blockpos1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
world.setBlockState(blockpos1, ((BlockSilverfish.EnumType)iblockstate.getValue(BlockSilverfish.VARIANT)).getModelBlock(), 3);
|
||||
}
|
||||
|
||||
if (random.nextBoolean())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntitySpectralArrow;
|
||||
import net.minecraft.entity.projectile.EntityTippedArrow;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntitySkeleton extends AbstractSkeleton
|
||||
{
|
||||
public EntitySkeleton(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
public static void registerFixesSkeleton(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntitySkeleton.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_SKELETON;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SKELETON_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_SKELETON_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SKELETON_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SKELETON_STEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mob's health reaches 0.
|
||||
*/
|
||||
public void onDeath(DamageSource cause)
|
||||
{
|
||||
super.onDeath(cause);
|
||||
|
||||
if (cause.getTrueSource() instanceof EntityCreeper)
|
||||
{
|
||||
EntityCreeper entitycreeper = (EntityCreeper)cause.getTrueSource();
|
||||
|
||||
if (entitycreeper.getPowered() && entitycreeper.ableToCauseSkullDrop())
|
||||
{
|
||||
entitycreeper.incrementDroppedSkulls();
|
||||
this.entityDropItem(new ItemStack(Items.SKULL, 1, 0), 0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected EntityArrow getArrow(float p_190726_1_)
|
||||
{
|
||||
ItemStack itemstack = this.getItemStackFromSlot(EntityEquipmentSlot.OFFHAND);
|
||||
|
||||
if (itemstack.getItem() == Items.SPECTRAL_ARROW)
|
||||
{
|
||||
EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(this.world, this);
|
||||
entityspectralarrow.setEnchantmentEffectsFromEntity(this, p_190726_1_);
|
||||
return entityspectralarrow;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityArrow entityarrow = super.getArrow(p_190726_1_);
|
||||
|
||||
if (itemstack.getItem() == Items.TIPPED_ARROW && entityarrow instanceof EntityTippedArrow)
|
||||
{
|
||||
((EntityTippedArrow)entityarrow).setPotionEffect(itemstack);
|
||||
}
|
||||
|
||||
return entityarrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,676 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIFindEntityNearest;
|
||||
import net.minecraft.entity.ai.EntityAIFindEntityNearestPlayer;
|
||||
import net.minecraft.entity.ai.EntityMoveHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldType;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntitySlime extends EntityLiving implements IMob
|
||||
{
|
||||
private static final DataParameter<Integer> SLIME_SIZE = EntityDataManager.<Integer>createKey(EntitySlime.class, DataSerializers.VARINT);
|
||||
public float squishAmount;
|
||||
public float squishFactor;
|
||||
public float prevSquishFactor;
|
||||
private boolean wasOnGround;
|
||||
|
||||
public EntitySlime(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.moveHelper = new EntitySlime.SlimeMoveHelper(this);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntitySlime.AISlimeFloat(this));
|
||||
this.tasks.addTask(2, new EntitySlime.AISlimeAttack(this));
|
||||
this.tasks.addTask(3, new EntitySlime.AISlimeFaceRandom(this));
|
||||
this.tasks.addTask(5, new EntitySlime.AISlimeHop(this));
|
||||
this.targetTasks.addTask(1, new EntityAIFindEntityNearestPlayer(this));
|
||||
this.targetTasks.addTask(3, new EntityAIFindEntityNearest(this, EntityIronGolem.class));
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(SLIME_SIZE, Integer.valueOf(1));
|
||||
}
|
||||
|
||||
protected void setSlimeSize(int size, boolean resetHealth)
|
||||
{
|
||||
this.dataManager.set(SLIME_SIZE, Integer.valueOf(size));
|
||||
this.setSize(0.51000005F * (float)size, 0.51000005F * (float)size);
|
||||
this.setPosition(this.posX, this.posY, this.posZ);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue((double)(size * size));
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue((double)(0.2F + 0.1F * (float)size));
|
||||
|
||||
if (resetHealth)
|
||||
{
|
||||
this.setHealth(this.getMaxHealth());
|
||||
}
|
||||
|
||||
this.experienceValue = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the slime.
|
||||
*/
|
||||
public int getSlimeSize()
|
||||
{
|
||||
return ((Integer)this.dataManager.get(SLIME_SIZE)).intValue();
|
||||
}
|
||||
|
||||
public static void registerFixesSlime(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntitySlime.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("Size", this.getSlimeSize() - 1);
|
||||
compound.setBoolean("wasOnGround", this.wasOnGround);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
int i = compound.getInteger("Size");
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
|
||||
this.setSlimeSize(i + 1, false);
|
||||
this.wasOnGround = compound.getBoolean("wasOnGround");
|
||||
}
|
||||
|
||||
public boolean isSmallSlime()
|
||||
{
|
||||
return this.getSlimeSize() <= 1;
|
||||
}
|
||||
|
||||
protected EnumParticleTypes getParticleType()
|
||||
{
|
||||
return EnumParticleTypes.SLIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
if (!this.world.isRemote && this.world.getDifficulty() == EnumDifficulty.PEACEFUL && this.getSlimeSize() > 0)
|
||||
{
|
||||
this.isDead = true;
|
||||
}
|
||||
|
||||
this.squishFactor += (this.squishAmount - this.squishFactor) * 0.5F;
|
||||
this.prevSquishFactor = this.squishFactor;
|
||||
super.onUpdate();
|
||||
|
||||
if (this.onGround && !this.wasOnGround)
|
||||
{
|
||||
int i = this.getSlimeSize();
|
||||
if (spawnCustomParticles()) { i = 0; } // don't spawn particles if it's handled by the implementation itself
|
||||
for (int j = 0; j < i * 8; ++j)
|
||||
{
|
||||
float f = this.rand.nextFloat() * ((float)Math.PI * 2F);
|
||||
float f1 = this.rand.nextFloat() * 0.5F + 0.5F;
|
||||
float f2 = MathHelper.sin(f) * (float)i * 0.5F * f1;
|
||||
float f3 = MathHelper.cos(f) * (float)i * 0.5F * f1;
|
||||
World world = this.world;
|
||||
EnumParticleTypes enumparticletypes = this.getParticleType();
|
||||
double d0 = this.posX + (double)f2;
|
||||
double d1 = this.posZ + (double)f3;
|
||||
world.spawnParticle(enumparticletypes, d0, this.getEntityBoundingBox().minY, d1, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
this.playSound(this.getSquishSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) / 0.8F);
|
||||
this.squishAmount = -0.5F;
|
||||
}
|
||||
else if (!this.onGround && this.wasOnGround)
|
||||
{
|
||||
this.squishAmount = 1.0F;
|
||||
}
|
||||
|
||||
this.wasOnGround = this.onGround;
|
||||
this.alterSquishAmount();
|
||||
}
|
||||
|
||||
protected void alterSquishAmount()
|
||||
{
|
||||
this.squishAmount *= 0.6F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of time the slime needs to wait between jumps.
|
||||
*/
|
||||
protected int getJumpDelay()
|
||||
{
|
||||
return this.rand.nextInt(20) + 10;
|
||||
}
|
||||
|
||||
protected EntitySlime createInstance()
|
||||
{
|
||||
return new EntitySlime(this.world);
|
||||
}
|
||||
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
if (SLIME_SIZE.equals(key))
|
||||
{
|
||||
int i = this.getSlimeSize();
|
||||
this.setSize(0.51000005F * (float)i, 0.51000005F * (float)i);
|
||||
this.rotationYaw = this.rotationYawHead;
|
||||
this.renderYawOffset = this.rotationYawHead;
|
||||
|
||||
if (this.isInWater() && this.rand.nextInt(20) == 0)
|
||||
{
|
||||
this.doWaterSplashEffect();
|
||||
}
|
||||
}
|
||||
|
||||
super.notifyDataManagerChange(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will get destroyed next tick.
|
||||
*/
|
||||
public void setDead()
|
||||
{
|
||||
int i = this.getSlimeSize();
|
||||
|
||||
if (!this.world.isRemote && i > 1 && this.getHealth() <= 0.0F)
|
||||
{
|
||||
int j = 2 + this.rand.nextInt(3);
|
||||
|
||||
for (int k = 0; k < j; ++k)
|
||||
{
|
||||
float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F;
|
||||
float f1 = ((float)(k / 2) - 0.5F) * (float)i / 4.0F;
|
||||
EntitySlime entityslime = this.createInstance();
|
||||
|
||||
if (this.hasCustomName())
|
||||
{
|
||||
entityslime.setCustomNameTag(this.getCustomNameTag());
|
||||
}
|
||||
|
||||
if (this.isNoDespawnRequired())
|
||||
{
|
||||
entityslime.enablePersistence();
|
||||
}
|
||||
|
||||
entityslime.setSlimeSize(i / 2, true);
|
||||
entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.nextFloat() * 360.0F, 0.0F);
|
||||
this.world.spawnEntity(entityslime);
|
||||
}
|
||||
}
|
||||
|
||||
super.setDead();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a velocity to the entities, to push them away from eachother.
|
||||
*/
|
||||
public void applyEntityCollision(Entity entityIn)
|
||||
{
|
||||
super.applyEntityCollision(entityIn);
|
||||
|
||||
if (entityIn instanceof EntityIronGolem && this.canDamagePlayer())
|
||||
{
|
||||
this.dealDamage((EntityLivingBase)entityIn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a player entity when they collide with an entity
|
||||
*/
|
||||
public void onCollideWithPlayer(EntityPlayer entityIn)
|
||||
{
|
||||
if (this.canDamagePlayer())
|
||||
{
|
||||
this.dealDamage(entityIn);
|
||||
}
|
||||
}
|
||||
|
||||
protected void dealDamage(EntityLivingBase entityIn)
|
||||
{
|
||||
int i = this.getSlimeSize();
|
||||
|
||||
if (this.canEntityBeSeen(entityIn) && this.getDistanceSq(entityIn) < 0.6D * (double)i * 0.6D * (double)i && entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), (float)this.getAttackStrength()))
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F);
|
||||
this.applyEnchantments(this, entityIn);
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.625F * this.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates weather the slime is able to damage the player (based upon the slime's size)
|
||||
*/
|
||||
protected boolean canDamagePlayer()
|
||||
{
|
||||
return !this.isSmallSlime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of damage dealt to the player when "attacked" by the slime.
|
||||
*/
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return this.getSlimeSize();
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_SLIME_HURT : SoundEvents.ENTITY_SLIME_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_SLIME_DEATH : SoundEvents.ENTITY_SLIME_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getSquishSound()
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_SLIME_SQUISH : SoundEvents.ENTITY_SLIME_SQUISH;
|
||||
}
|
||||
|
||||
protected Item getDropItem()
|
||||
{
|
||||
return this.getSlimeSize() == 1 ? Items.SLIME_BALL : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return this.getSlimeSize() == 1 ? LootTableList.ENTITIES_SLIME : LootTableList.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(MathHelper.floor(this.posX), 0, MathHelper.floor(this.posZ));
|
||||
Chunk chunk = this.world.getChunkFromBlockCoords(blockpos);
|
||||
|
||||
if (this.world.getWorldInfo().getTerrainType().handleSlimeSpawnReduction(rand, world))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.world.getDifficulty() != EnumDifficulty.PEACEFUL)
|
||||
{
|
||||
Biome biome = this.world.getBiome(blockpos);
|
||||
|
||||
if (biome == Biomes.SWAMPLAND && this.posY > 50.0D && this.posY < 70.0D && this.rand.nextFloat() < 0.5F && this.rand.nextFloat() < this.world.getCurrentMoonPhaseFactor() && this.world.getLightFromNeighbors(new BlockPos(this)) <= this.rand.nextInt(8))
|
||||
{
|
||||
return super.getCanSpawnHere();
|
||||
}
|
||||
|
||||
if (this.rand.nextInt(10) == 0 && chunk.getRandomWithSeed(987234911L).nextInt(10) == 0 && this.posY < 40.0D)
|
||||
{
|
||||
return super.getCanSpawnHere();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the volume for the sounds this mob makes.
|
||||
*/
|
||||
protected float getSoundVolume()
|
||||
{
|
||||
return 0.4F * (float)this.getSlimeSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
|
||||
* use in wolves.
|
||||
*/
|
||||
public int getVerticalFaceSpeed()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the slime makes a sound when it jumps (based upon the slime's size)
|
||||
*/
|
||||
protected boolean makesSoundOnJump()
|
||||
{
|
||||
return this.getSlimeSize() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this entity to do an upwards motion (jumping).
|
||||
*/
|
||||
protected void jump()
|
||||
{
|
||||
this.motionY = 0.41999998688697815D;
|
||||
this.isAirBorne = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
int i = this.rand.nextInt(3);
|
||||
|
||||
if (i < 2 && this.rand.nextFloat() < 0.5F * difficulty.getClampedAdditionalDifficulty())
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
int j = 1 << i;
|
||||
this.setSlimeSize(j, true);
|
||||
return super.onInitialSpawn(difficulty, livingdata);
|
||||
}
|
||||
|
||||
protected SoundEvent getJumpSound()
|
||||
{
|
||||
return this.isSmallSlime() ? SoundEvents.ENTITY_SMALL_SLIME_JUMP : SoundEvents.ENTITY_SLIME_JUMP;
|
||||
}
|
||||
|
||||
/* ======================================== FORGE START =====================================*/
|
||||
/**
|
||||
* Called when the slime spawns particles on landing, see onUpdate.
|
||||
* Return true to prevent the spawning of the default particles.
|
||||
*/
|
||||
protected boolean spawnCustomParticles() { return false; }
|
||||
/* ======================================== FORGE END =====================================*/
|
||||
|
||||
static class AISlimeAttack extends EntityAIBase
|
||||
{
|
||||
private final EntitySlime slime;
|
||||
private int growTieredTimer;
|
||||
|
||||
public AISlimeAttack(EntitySlime slimeIn)
|
||||
{
|
||||
this.slime = slimeIn;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.slime.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(entitylivingbase instanceof EntityPlayer) || !((EntityPlayer)entitylivingbase).capabilities.disableDamage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.growTieredTimer = 300;
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.slime.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (entitylivingbase instanceof EntityPlayer && ((EntityPlayer)entitylivingbase).capabilities.disableDamage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return --this.growTieredTimer > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.slime.faceEntity(this.slime.getAttackTarget(), 10.0F, 10.0F);
|
||||
((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setDirection(this.slime.rotationYaw, this.slime.canDamagePlayer());
|
||||
}
|
||||
}
|
||||
|
||||
static class AISlimeFaceRandom extends EntityAIBase
|
||||
{
|
||||
private final EntitySlime slime;
|
||||
private float chosenDegrees;
|
||||
private int nextRandomizeTime;
|
||||
|
||||
public AISlimeFaceRandom(EntitySlime slimeIn)
|
||||
{
|
||||
this.slime = slimeIn;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.slime.getAttackTarget() == null && (this.slime.onGround || this.slime.isInWater() || this.slime.isInLava() || this.slime.isPotionActive(MobEffects.LEVITATION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (--this.nextRandomizeTime <= 0)
|
||||
{
|
||||
this.nextRandomizeTime = 40 + this.slime.getRNG().nextInt(60);
|
||||
this.chosenDegrees = (float)this.slime.getRNG().nextInt(360);
|
||||
}
|
||||
|
||||
((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setDirection(this.chosenDegrees, false);
|
||||
}
|
||||
}
|
||||
|
||||
static class AISlimeFloat extends EntityAIBase
|
||||
{
|
||||
private final EntitySlime slime;
|
||||
|
||||
public AISlimeFloat(EntitySlime slimeIn)
|
||||
{
|
||||
this.slime = slimeIn;
|
||||
this.setMutexBits(5);
|
||||
((PathNavigateGround)slimeIn.getNavigator()).setCanSwim(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.slime.isInWater() || this.slime.isInLava();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.slime.getRNG().nextFloat() < 0.8F)
|
||||
{
|
||||
this.slime.getJumpHelper().setJumping();
|
||||
}
|
||||
|
||||
((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setSpeed(1.2D);
|
||||
}
|
||||
}
|
||||
|
||||
static class AISlimeHop extends EntityAIBase
|
||||
{
|
||||
private final EntitySlime slime;
|
||||
|
||||
public AISlimeHop(EntitySlime slimeIn)
|
||||
{
|
||||
this.slime = slimeIn;
|
||||
this.setMutexBits(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setSpeed(1.0D);
|
||||
}
|
||||
}
|
||||
|
||||
static class SlimeMoveHelper extends EntityMoveHelper
|
||||
{
|
||||
private float yRot;
|
||||
private int jumpDelay;
|
||||
private final EntitySlime slime;
|
||||
private boolean isAggressive;
|
||||
|
||||
public SlimeMoveHelper(EntitySlime slimeIn)
|
||||
{
|
||||
super(slimeIn);
|
||||
this.slime = slimeIn;
|
||||
this.yRot = 180.0F * slimeIn.rotationYaw / (float)Math.PI;
|
||||
}
|
||||
|
||||
public void setDirection(float p_179920_1_, boolean p_179920_2_)
|
||||
{
|
||||
this.yRot = p_179920_1_;
|
||||
this.isAggressive = p_179920_2_;
|
||||
}
|
||||
|
||||
public void setSpeed(double speedIn)
|
||||
{
|
||||
this.speed = speedIn;
|
||||
this.action = EntityMoveHelper.Action.MOVE_TO;
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
this.entity.rotationYaw = this.limitAngle(this.entity.rotationYaw, this.yRot, 90.0F);
|
||||
this.entity.rotationYawHead = this.entity.rotationYaw;
|
||||
this.entity.renderYawOffset = this.entity.rotationYaw;
|
||||
|
||||
if (this.action != EntityMoveHelper.Action.MOVE_TO)
|
||||
{
|
||||
this.entity.setMoveForward(0.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
|
||||
if (this.entity.onGround)
|
||||
{
|
||||
this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue()));
|
||||
|
||||
if (this.jumpDelay-- <= 0)
|
||||
{
|
||||
this.jumpDelay = this.slime.getJumpDelay();
|
||||
|
||||
if (this.isAggressive)
|
||||
{
|
||||
this.jumpDelay /= 3;
|
||||
}
|
||||
|
||||
this.slime.getJumpHelper().setJumping();
|
||||
|
||||
if (this.slime.makesSoundOnJump())
|
||||
{
|
||||
this.slime.playSound(this.slime.getJumpSound(), this.slime.getSoundVolume(), ((this.slime.getRNG().nextFloat() - this.slime.getRNG().nextFloat()) * 0.2F + 1.0F) * 0.8F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.slime.moveStrafing = 0.0F;
|
||||
this.slime.moveForward = 0.0F;
|
||||
this.entity.setAIMoveSpeed(0.0F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackRanged;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntitySnowball;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntitySnowman extends EntityGolem implements IRangedAttackMob, net.minecraftforge.common.IShearable
|
||||
{
|
||||
private static final DataParameter<Byte> PUMPKIN_EQUIPPED = EntityDataManager.<Byte>createKey(EntitySnowman.class, DataSerializers.BYTE);
|
||||
|
||||
public EntitySnowman(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.7F, 1.9F);
|
||||
}
|
||||
|
||||
public static void registerFixesSnowman(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntitySnowman.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAIAttackRanged(this, 1.25D, 20, 10.0F));
|
||||
this.tasks.addTask(2, new EntityAIWanderAvoidWater(this, 1.0D, 1.0000001E-5F));
|
||||
this.tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
|
||||
this.tasks.addTask(4, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityLiving.class, 10, true, false, IMob.MOB_SELECTOR));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(4.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.20000000298023224D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(PUMPKIN_EQUIPPED, Byte.valueOf((byte)16));
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setBoolean("Pumpkin", this.isPumpkinEquipped());
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
|
||||
if (compound.hasKey("Pumpkin"))
|
||||
{
|
||||
this.setPumpkinEquipped(compound.getBoolean("Pumpkin"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
super.onLivingUpdate();
|
||||
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
int i = MathHelper.floor(this.posX);
|
||||
int j = MathHelper.floor(this.posY);
|
||||
int k = MathHelper.floor(this.posZ);
|
||||
|
||||
if (this.isWet())
|
||||
{
|
||||
this.attackEntityFrom(DamageSource.DROWN, 1.0F);
|
||||
}
|
||||
|
||||
if (this.world.getBiome(new BlockPos(i, 0, k)).getTemperature(new BlockPos(i, j, k)) > 1.0F)
|
||||
{
|
||||
this.attackEntityFrom(DamageSource.ON_FIRE, 1.0F);
|
||||
}
|
||||
|
||||
if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int l = 0; l < 4; ++l)
|
||||
{
|
||||
i = MathHelper.floor(this.posX + (double)((float)(l % 2 * 2 - 1) * 0.25F));
|
||||
j = MathHelper.floor(this.posY);
|
||||
k = MathHelper.floor(this.posZ + (double)((float)(l / 2 % 2 * 2 - 1) * 0.25F));
|
||||
BlockPos blockpos = new BlockPos(i, j, k);
|
||||
|
||||
if (this.world.getBlockState(blockpos).getMaterial() == Material.AIR && this.world.getBiome(blockpos).getTemperature(blockpos) < 0.8F && Blocks.SNOW_LAYER.canPlaceBlockAt(this.world, blockpos))
|
||||
{
|
||||
this.world.setBlockState(blockpos, Blocks.SNOW_LAYER.getDefaultState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_SNOWMAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack the specified entity using a ranged attack.
|
||||
*/
|
||||
public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor)
|
||||
{
|
||||
EntitySnowball entitysnowball = new EntitySnowball(this.world, this);
|
||||
double d0 = target.posY + (double)target.getEyeHeight() - 1.100000023841858D;
|
||||
double d1 = target.posX - this.posX;
|
||||
double d2 = d0 - entitysnowball.posY;
|
||||
double d3 = target.posZ - this.posZ;
|
||||
float f = MathHelper.sqrt(d1 * d1 + d3 * d3) * 0.2F;
|
||||
entitysnowball.shoot(d1, d2 + (double)f, d3, 1.6F, 12.0F);
|
||||
this.playSound(SoundEvents.ENTITY_SNOWMAN_SHOOT, 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
|
||||
this.world.spawnEntity(entitysnowball);
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 1.7F;
|
||||
}
|
||||
|
||||
protected boolean processInteract(EntityPlayer player, EnumHand hand)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItem(hand);
|
||||
|
||||
if (false && itemstack.getItem() == Items.SHEARS && this.isPumpkinEquipped() && !this.world.isRemote) //Forge: Moved to onSheared
|
||||
{
|
||||
this.setPumpkinEquipped(false);
|
||||
itemstack.damageItem(1, player);
|
||||
}
|
||||
|
||||
return super.processInteract(player, hand);
|
||||
}
|
||||
|
||||
public boolean isPumpkinEquipped()
|
||||
{
|
||||
return (((Byte)this.dataManager.get(PUMPKIN_EQUIPPED)).byteValue() & 16) != 0;
|
||||
}
|
||||
|
||||
public void setPumpkinEquipped(boolean pumpkinEquipped)
|
||||
{
|
||||
byte b0 = ((Byte)this.dataManager.get(PUMPKIN_EQUIPPED)).byteValue();
|
||||
|
||||
if (pumpkinEquipped)
|
||||
{
|
||||
this.dataManager.set(PUMPKIN_EQUIPPED, Byte.valueOf((byte)(b0 | 16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dataManager.set(PUMPKIN_EQUIPPED, Byte.valueOf((byte)(b0 & -17)));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SNOWMAN_AMBIENT;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_SNOWMAN_HURT;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SNOWMAN_DEATH;
|
||||
}
|
||||
|
||||
@Override public boolean isShearable(ItemStack item, net.minecraft.world.IBlockAccess world, BlockPos pos) { return this.isPumpkinEquipped(); }
|
||||
@Override
|
||||
public java.util.List<ItemStack> onSheared(ItemStack item, net.minecraft.world.IBlockAccess world, BlockPos pos, int fortune)
|
||||
{
|
||||
this.setPumpkinEquipped(false);
|
||||
return com.google.common.collect.Lists.newArrayList();
|
||||
}
|
||||
|
||||
public void setSwingingArms(boolean swingingArms)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
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.util.EnumParticleTypes;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public abstract class EntitySpellcasterIllager extends AbstractIllager
|
||||
{
|
||||
private static final DataParameter<Byte> SPELL = EntityDataManager.<Byte>createKey(EntitySpellcasterIllager.class, DataSerializers.BYTE);
|
||||
protected int spellTicks;
|
||||
private EntitySpellcasterIllager.SpellType activeSpell = EntitySpellcasterIllager.SpellType.NONE;
|
||||
|
||||
public EntitySpellcasterIllager(World p_i47506_1_)
|
||||
{
|
||||
super(p_i47506_1_);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(SPELL, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.spellTicks = compound.getInteger("SpellTicks");
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("SpellTicks", this.spellTicks);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public AbstractIllager.IllagerArmPose getArmPose()
|
||||
{
|
||||
return this.isSpellcasting() ? AbstractIllager.IllagerArmPose.SPELLCASTING : AbstractIllager.IllagerArmPose.CROSSED;
|
||||
}
|
||||
|
||||
public boolean isSpellcasting()
|
||||
{
|
||||
if (this.world.isRemote)
|
||||
{
|
||||
return ((Byte)this.dataManager.get(SPELL)).byteValue() > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.spellTicks > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSpellType(EntitySpellcasterIllager.SpellType spellType)
|
||||
{
|
||||
this.activeSpell = spellType;
|
||||
this.dataManager.set(SPELL, Byte.valueOf((byte)spellType.id));
|
||||
}
|
||||
|
||||
protected EntitySpellcasterIllager.SpellType getSpellType()
|
||||
{
|
||||
return !this.world.isRemote ? this.activeSpell : EntitySpellcasterIllager.SpellType.getFromId(((Byte)this.dataManager.get(SPELL)).byteValue());
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
super.updateAITasks();
|
||||
|
||||
if (this.spellTicks > 0)
|
||||
{
|
||||
--this.spellTicks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
|
||||
if (this.world.isRemote && this.isSpellcasting())
|
||||
{
|
||||
EntitySpellcasterIllager.SpellType entityspellcasterillager$spelltype = this.getSpellType();
|
||||
double d0 = entityspellcasterillager$spelltype.particleSpeed[0];
|
||||
double d1 = entityspellcasterillager$spelltype.particleSpeed[1];
|
||||
double d2 = entityspellcasterillager$spelltype.particleSpeed[2];
|
||||
float f = this.renderYawOffset * 0.017453292F + MathHelper.cos((float)this.ticksExisted * 0.6662F) * 0.25F;
|
||||
float f1 = MathHelper.cos(f);
|
||||
float f2 = MathHelper.sin(f);
|
||||
this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, this.posX + (double)f1 * 0.6D, this.posY + 1.8D, this.posZ + (double)f2 * 0.6D, d0, d1, d2);
|
||||
this.world.spawnParticle(EnumParticleTypes.SPELL_MOB, this.posX - (double)f1 * 0.6D, this.posY + 1.8D, this.posZ - (double)f2 * 0.6D, d0, d1, d2);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getSpellTicks()
|
||||
{
|
||||
return this.spellTicks;
|
||||
}
|
||||
|
||||
protected abstract SoundEvent getSpellSound();
|
||||
|
||||
public class AICastingApell extends EntityAIBase
|
||||
{
|
||||
public AICastingApell()
|
||||
{
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return EntitySpellcasterIllager.this.getSpellTicks() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
EntitySpellcasterIllager.this.navigator.clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
EntitySpellcasterIllager.this.setSpellType(EntitySpellcasterIllager.SpellType.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (EntitySpellcasterIllager.this.getAttackTarget() != null)
|
||||
{
|
||||
EntitySpellcasterIllager.this.getLookHelper().setLookPositionWithEntity(EntitySpellcasterIllager.this.getAttackTarget(), (float)EntitySpellcasterIllager.this.getHorizontalFaceSpeed(), (float)EntitySpellcasterIllager.this.getVerticalFaceSpeed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AIUseSpell extends EntityAIBase
|
||||
{
|
||||
protected int spellWarmup;
|
||||
protected int spellCooldown;
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (EntitySpellcasterIllager.this.getAttackTarget() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (EntitySpellcasterIllager.this.isSpellcasting())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntitySpellcasterIllager.this.ticksExisted >= this.spellCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return EntitySpellcasterIllager.this.getAttackTarget() != null && this.spellWarmup > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.spellWarmup = this.getCastWarmupTime();
|
||||
EntitySpellcasterIllager.this.spellTicks = this.getCastingTime();
|
||||
this.spellCooldown = EntitySpellcasterIllager.this.ticksExisted + this.getCastingInterval();
|
||||
SoundEvent soundevent = this.getSpellPrepareSound();
|
||||
|
||||
if (soundevent != null)
|
||||
{
|
||||
EntitySpellcasterIllager.this.playSound(soundevent, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
EntitySpellcasterIllager.this.setSpellType(this.getSpellType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.spellWarmup;
|
||||
|
||||
if (this.spellWarmup == 0)
|
||||
{
|
||||
this.castSpell();
|
||||
EntitySpellcasterIllager.this.playSound(EntitySpellcasterIllager.this.getSpellSound(), 1.0F, 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void castSpell();
|
||||
|
||||
protected int getCastWarmupTime()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
protected abstract int getCastingTime();
|
||||
|
||||
protected abstract int getCastingInterval();
|
||||
|
||||
@Nullable
|
||||
protected abstract SoundEvent getSpellPrepareSound();
|
||||
|
||||
protected abstract EntitySpellcasterIllager.SpellType getSpellType();
|
||||
}
|
||||
|
||||
public static enum SpellType
|
||||
{
|
||||
NONE(0, 0.0D, 0.0D, 0.0D),
|
||||
SUMMON_VEX(1, 0.7D, 0.7D, 0.8D),
|
||||
FANGS(2, 0.4D, 0.3D, 0.35D),
|
||||
WOLOLO(3, 0.7D, 0.5D, 0.2D),
|
||||
DISAPPEAR(4, 0.3D, 0.3D, 0.8D),
|
||||
BLINDNESS(5, 0.1D, 0.1D, 0.2D);
|
||||
|
||||
private final int id;
|
||||
/** Particle motion speed. An array with 3 values: x, y, and z. */
|
||||
private final double[] particleSpeed;
|
||||
|
||||
private SpellType(int idIn, double xParticleSpeed, double yParticleSpeed, double zParticleSpeed)
|
||||
{
|
||||
this.id = idIn;
|
||||
this.particleSpeed = new double[] {xParticleSpeed, yParticleSpeed, zParticleSpeed};
|
||||
}
|
||||
|
||||
public static EntitySpellcasterIllager.SpellType getFromId(int idIn)
|
||||
{
|
||||
for (EntitySpellcasterIllager.SpellType entityspellcasterillager$spelltype : values())
|
||||
{
|
||||
if (idIn == entityspellcasterillager$spelltype.id)
|
||||
{
|
||||
return entityspellcasterillager$spelltype;
|
||||
}
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILeapAtTarget;
|
||||
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.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNavigateClimber;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntitySpider extends EntityMob
|
||||
{
|
||||
private static final DataParameter<Byte> CLIMBING = EntityDataManager.<Byte>createKey(EntitySpider.class, DataSerializers.BYTE);
|
||||
|
||||
public EntitySpider(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(1.4F, 0.9F);
|
||||
}
|
||||
|
||||
public static void registerFixesSpider(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntitySpider.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F));
|
||||
this.tasks.addTask(4, new EntitySpider.AISpiderAttack(this));
|
||||
this.tasks.addTask(5, new EntityAIWanderAvoidWater(this, 0.8D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(6, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntitySpider.AISpiderTarget(this, EntityPlayer.class));
|
||||
this.targetTasks.addTask(3, new EntitySpider.AISpiderTarget(this, EntityIronGolem.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y offset from the entity's position for any entity riding this one.
|
||||
*/
|
||||
public double getMountedYOffset()
|
||||
{
|
||||
return (double)(this.height * 0.5F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new PathNavigateGround instance
|
||||
*/
|
||||
protected PathNavigate createNavigator(World worldIn)
|
||||
{
|
||||
return new PathNavigateClimber(this, worldIn);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(CLIMBING, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
super.onUpdate();
|
||||
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
this.setBesideClimbableBlock(this.collidedHorizontally);
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(16.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.30000001192092896D);
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SPIDER_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_SPIDER_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_SPIDER_DEATH;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(SoundEvents.ENTITY_SPIDER_STEP, 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_SPIDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this entity should move as if it were on a ladder (either because it's actually on a ladder, or
|
||||
* for AI reasons)
|
||||
*/
|
||||
public boolean isOnLadder()
|
||||
{
|
||||
return this.isBesideClimbableBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Entity inside a web block.
|
||||
*/
|
||||
public void setInWeb()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.ARTHROPOD;
|
||||
}
|
||||
|
||||
public boolean isPotionApplicable(PotionEffect potioneffectIn)
|
||||
{
|
||||
return potioneffectIn.getPotion() == MobEffects.POISON ? false : super.isPotionApplicable(potioneffectIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the WatchableObject (Byte) is 0x01 otherwise returns false. The WatchableObject is updated using
|
||||
* setBesideClimableBlock.
|
||||
*/
|
||||
public boolean isBesideClimbableBlock()
|
||||
{
|
||||
return (((Byte)this.dataManager.get(CLIMBING)).byteValue() & 1) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the WatchableObject (Byte) created in entityInit(), setting it to 0x01 if par1 is true or 0x00 if it is
|
||||
* false.
|
||||
*/
|
||||
public void setBesideClimbableBlock(boolean climbing)
|
||||
{
|
||||
byte b0 = ((Byte)this.dataManager.get(CLIMBING)).byteValue();
|
||||
|
||||
if (climbing)
|
||||
{
|
||||
b0 = (byte)(b0 | 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
b0 = (byte)(b0 & -2);
|
||||
}
|
||||
|
||||
this.dataManager.set(CLIMBING, Byte.valueOf(b0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
livingdata = super.onInitialSpawn(difficulty, livingdata);
|
||||
|
||||
if (this.world.rand.nextInt(100) == 0)
|
||||
{
|
||||
EntitySkeleton entityskeleton = new EntitySkeleton(this.world);
|
||||
entityskeleton.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, 0.0F);
|
||||
entityskeleton.onInitialSpawn(difficulty, (IEntityLivingData)null);
|
||||
this.world.spawnEntity(entityskeleton);
|
||||
entityskeleton.startRiding(this);
|
||||
}
|
||||
|
||||
if (livingdata == null)
|
||||
{
|
||||
livingdata = new EntitySpider.GroupData();
|
||||
|
||||
if (this.world.getDifficulty() == EnumDifficulty.HARD && this.world.rand.nextFloat() < 0.1F * difficulty.getClampedAdditionalDifficulty())
|
||||
{
|
||||
((EntitySpider.GroupData)livingdata).setRandomEffect(this.world.rand);
|
||||
}
|
||||
}
|
||||
|
||||
if (livingdata instanceof EntitySpider.GroupData)
|
||||
{
|
||||
Potion potion = ((EntitySpider.GroupData)livingdata).effect;
|
||||
|
||||
if (potion != null)
|
||||
{
|
||||
this.addPotionEffect(new PotionEffect(potion, Integer.MAX_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
return livingdata;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 0.65F;
|
||||
}
|
||||
|
||||
static class AISpiderAttack extends EntityAIAttackMelee
|
||||
{
|
||||
public AISpiderAttack(EntitySpider spider)
|
||||
{
|
||||
super(spider, 1.0D, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
float f = this.attacker.getBrightness();
|
||||
|
||||
if (f >= 0.5F && this.attacker.getRNG().nextInt(100) == 0)
|
||||
{
|
||||
this.attacker.setAttackTarget((EntityLivingBase)null);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.shouldContinueExecuting();
|
||||
}
|
||||
}
|
||||
|
||||
protected double getAttackReachSqr(EntityLivingBase attackTarget)
|
||||
{
|
||||
return (double)(4.0F + attackTarget.width);
|
||||
}
|
||||
}
|
||||
|
||||
static class AISpiderTarget<T extends EntityLivingBase> extends EntityAINearestAttackableTarget<T>
|
||||
{
|
||||
public AISpiderTarget(EntitySpider spider, Class<T> classTarget)
|
||||
{
|
||||
super(spider, classTarget, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
float f = this.taskOwner.getBrightness();
|
||||
return f >= 0.5F ? false : super.shouldExecute();
|
||||
}
|
||||
}
|
||||
|
||||
public static class GroupData implements IEntityLivingData
|
||||
{
|
||||
public Potion effect;
|
||||
|
||||
public void setRandomEffect(Random rand)
|
||||
{
|
||||
int i = rand.nextInt(5);
|
||||
|
||||
if (i <= 1)
|
||||
{
|
||||
this.effect = MobEffects.SPEED;
|
||||
}
|
||||
else if (i <= 2)
|
||||
{
|
||||
this.effect = MobEffects.STRENGTH;
|
||||
}
|
||||
else if (i <= 3)
|
||||
{
|
||||
this.effect = MobEffects.REGENERATION;
|
||||
}
|
||||
else if (i <= 4)
|
||||
{
|
||||
this.effect = MobEffects.INVISIBILITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityTippedArrow;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityStray extends AbstractSkeleton
|
||||
{
|
||||
public EntityStray(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
public static void registerFixesStray(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityStray.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's current position is a valid location to spawn this entity.
|
||||
*/
|
||||
public boolean getCanSpawnHere()
|
||||
{
|
||||
return super.getCanSpawnHere() && this.world.canSeeSky(new BlockPos(this));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_STRAY;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_STRAY_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_STRAY_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_STRAY_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_STRAY_STEP;
|
||||
}
|
||||
|
||||
protected EntityArrow getArrow(float p_190726_1_)
|
||||
{
|
||||
EntityArrow entityarrow = super.getArrow(p_190726_1_);
|
||||
|
||||
if (entityarrow instanceof EntityTippedArrow)
|
||||
{
|
||||
((EntityTippedArrow)entityarrow).addEffect(new PotionEffect(MobEffects.SLOWNESS, 600));
|
||||
}
|
||||
|
||||
return entityarrow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAITarget;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.ai.EntityMoveHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.util.math.Vec3d;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityVex extends EntityMob
|
||||
{
|
||||
protected static final DataParameter<Byte> VEX_FLAGS = EntityDataManager.<Byte>createKey(EntityVex.class, DataSerializers.BYTE);
|
||||
private EntityLiving owner;
|
||||
@Nullable
|
||||
private BlockPos boundOrigin;
|
||||
private boolean limitedLifespan;
|
||||
private int limitedLifeTicks;
|
||||
|
||||
public EntityVex(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.isImmuneToFire = true;
|
||||
this.moveHelper = new EntityVex.AIMoveControl(this);
|
||||
this.setSize(0.4F, 0.8F);
|
||||
this.experienceValue = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to move the entity towards the specified location.
|
||||
*/
|
||||
public void move(MoverType type, double x, double y, double z)
|
||||
{
|
||||
super.move(type, x, y, z);
|
||||
this.doBlockCollisions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
this.noClip = true;
|
||||
super.onUpdate();
|
||||
this.noClip = false;
|
||||
this.setNoGravity(true);
|
||||
|
||||
if (this.limitedLifespan && --this.limitedLifeTicks <= 0)
|
||||
{
|
||||
this.limitedLifeTicks = 20;
|
||||
this.attackEntityFrom(DamageSource.STARVE, 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
super.initEntityAI();
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(4, new EntityVex.AIChargeAttack());
|
||||
this.tasks.addTask(8, new EntityVex.AIMoveRandom());
|
||||
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F));
|
||||
this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[] {EntityVex.class}));
|
||||
this.targetTasks.addTask(2, new EntityVex.AICopyOwnerTarget(this));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(14.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(4.0D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(VEX_FLAGS, Byte.valueOf((byte)0));
|
||||
}
|
||||
|
||||
public static void registerFixesVex(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityVex.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
|
||||
if (compound.hasKey("BoundX"))
|
||||
{
|
||||
this.boundOrigin = new BlockPos(compound.getInteger("BoundX"), compound.getInteger("BoundY"), compound.getInteger("BoundZ"));
|
||||
}
|
||||
|
||||
if (compound.hasKey("LifeTicks"))
|
||||
{
|
||||
this.setLimitedLife(compound.getInteger("LifeTicks"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
|
||||
if (this.boundOrigin != null)
|
||||
{
|
||||
compound.setInteger("BoundX", this.boundOrigin.getX());
|
||||
compound.setInteger("BoundY", this.boundOrigin.getY());
|
||||
compound.setInteger("BoundZ", this.boundOrigin.getZ());
|
||||
}
|
||||
|
||||
if (this.limitedLifespan)
|
||||
{
|
||||
compound.setInteger("LifeTicks", this.limitedLifeTicks);
|
||||
}
|
||||
}
|
||||
|
||||
public EntityLiving getOwner()
|
||||
{
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPos getBoundOrigin()
|
||||
{
|
||||
return this.boundOrigin;
|
||||
}
|
||||
|
||||
public void setBoundOrigin(@Nullable BlockPos boundOriginIn)
|
||||
{
|
||||
this.boundOrigin = boundOriginIn;
|
||||
}
|
||||
|
||||
private boolean getVexFlag(int mask)
|
||||
{
|
||||
int i = ((Byte)this.dataManager.get(VEX_FLAGS)).byteValue();
|
||||
return (i & mask) != 0;
|
||||
}
|
||||
|
||||
private void setVexFlag(int mask, boolean value)
|
||||
{
|
||||
int i = ((Byte)this.dataManager.get(VEX_FLAGS)).byteValue();
|
||||
|
||||
if (value)
|
||||
{
|
||||
i = i | mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = i & ~mask;
|
||||
}
|
||||
|
||||
this.dataManager.set(VEX_FLAGS, Byte.valueOf((byte)(i & 255)));
|
||||
}
|
||||
|
||||
public boolean isCharging()
|
||||
{
|
||||
return this.getVexFlag(1);
|
||||
}
|
||||
|
||||
public void setCharging(boolean charging)
|
||||
{
|
||||
this.setVexFlag(1, charging);
|
||||
}
|
||||
|
||||
public void setOwner(EntityLiving ownerIn)
|
||||
{
|
||||
this.owner = ownerIn;
|
||||
}
|
||||
|
||||
public void setLimitedLife(int limitedLifeTicksIn)
|
||||
{
|
||||
this.limitedLifespan = true;
|
||||
this.limitedLifeTicks = limitedLifeTicksIn;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_VEX_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_VEX_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_VEX_HURT;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_VEX;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getBrightnessForRender()
|
||||
{
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets how bright this entity is.
|
||||
*/
|
||||
public float getBrightness()
|
||||
{
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
this.setEquipmentBasedOnDifficulty(difficulty);
|
||||
this.setEnchantmentBasedOnDifficulty(difficulty);
|
||||
return super.onInitialSpawn(difficulty, livingdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD));
|
||||
this.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0F);
|
||||
}
|
||||
|
||||
class AIChargeAttack extends EntityAIBase
|
||||
{
|
||||
public AIChargeAttack()
|
||||
{
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (EntityVex.this.getAttackTarget() != null && !EntityVex.this.getMoveHelper().isUpdating() && EntityVex.this.rand.nextInt(7) == 0)
|
||||
{
|
||||
return EntityVex.this.getDistanceSq(EntityVex.this.getAttackTarget()) > 4.0D;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return EntityVex.this.getMoveHelper().isUpdating() && EntityVex.this.isCharging() && EntityVex.this.getAttackTarget() != null && EntityVex.this.getAttackTarget().isEntityAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = EntityVex.this.getAttackTarget();
|
||||
Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F);
|
||||
EntityVex.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D);
|
||||
EntityVex.this.setCharging(true);
|
||||
EntityVex.this.playSound(SoundEvents.ENTITY_VEX_CHARGE, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
EntityVex.this.setCharging(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = EntityVex.this.getAttackTarget();
|
||||
|
||||
if (EntityVex.this.getEntityBoundingBox().intersects(entitylivingbase.getEntityBoundingBox()))
|
||||
{
|
||||
EntityVex.this.attackEntityAsMob(entitylivingbase);
|
||||
EntityVex.this.setCharging(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = EntityVex.this.getDistanceSq(entitylivingbase);
|
||||
|
||||
if (d0 < 9.0D)
|
||||
{
|
||||
Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F);
|
||||
EntityVex.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AICopyOwnerTarget extends EntityAITarget
|
||||
{
|
||||
public AICopyOwnerTarget(EntityCreature creature)
|
||||
{
|
||||
super(creature, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return EntityVex.this.owner != null && EntityVex.this.owner.getAttackTarget() != null && this.isSuitableTarget(EntityVex.this.owner.getAttackTarget(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
EntityVex.this.setAttackTarget(EntityVex.this.owner.getAttackTarget());
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
||||
|
||||
class AIMoveControl extends EntityMoveHelper
|
||||
{
|
||||
public AIMoveControl(EntityVex vex)
|
||||
{
|
||||
super(vex);
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
if (this.action == EntityMoveHelper.Action.MOVE_TO)
|
||||
{
|
||||
double d0 = this.posX - EntityVex.this.posX;
|
||||
double d1 = this.posY - EntityVex.this.posY;
|
||||
double d2 = this.posZ - EntityVex.this.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
d3 = (double)MathHelper.sqrt(d3);
|
||||
|
||||
if (d3 < EntityVex.this.getEntityBoundingBox().getAverageEdgeLength())
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
EntityVex.this.motionX *= 0.5D;
|
||||
EntityVex.this.motionY *= 0.5D;
|
||||
EntityVex.this.motionZ *= 0.5D;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityVex.this.motionX += d0 / d3 * 0.05D * this.speed;
|
||||
EntityVex.this.motionY += d1 / d3 * 0.05D * this.speed;
|
||||
EntityVex.this.motionZ += d2 / d3 * 0.05D * this.speed;
|
||||
|
||||
if (EntityVex.this.getAttackTarget() == null)
|
||||
{
|
||||
EntityVex.this.rotationYaw = -((float)MathHelper.atan2(EntityVex.this.motionX, EntityVex.this.motionZ)) * (180F / (float)Math.PI);
|
||||
EntityVex.this.renderYawOffset = EntityVex.this.rotationYaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d4 = EntityVex.this.getAttackTarget().posX - EntityVex.this.posX;
|
||||
double d5 = EntityVex.this.getAttackTarget().posZ - EntityVex.this.posZ;
|
||||
EntityVex.this.rotationYaw = -((float)MathHelper.atan2(d4, d5)) * (180F / (float)Math.PI);
|
||||
EntityVex.this.renderYawOffset = EntityVex.this.rotationYaw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AIMoveRandom extends EntityAIBase
|
||||
{
|
||||
public AIMoveRandom()
|
||||
{
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return !EntityVex.this.getMoveHelper().isUpdating() && EntityVex.this.rand.nextInt(7) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
BlockPos blockpos = EntityVex.this.getBoundOrigin();
|
||||
|
||||
if (blockpos == null)
|
||||
{
|
||||
blockpos = new BlockPos(EntityVex.this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.add(EntityVex.this.rand.nextInt(15) - 7, EntityVex.this.rand.nextInt(11) - 5, EntityVex.this.rand.nextInt(15) - 7);
|
||||
|
||||
if (EntityVex.this.world.isAirBlock(blockpos1))
|
||||
{
|
||||
EntityVex.this.moveHelper.setMoveTo((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 0.25D);
|
||||
|
||||
if (EntityVex.this.getAttackTarget() == null)
|
||||
{
|
||||
EntityVex.this.getLookHelper().setLookPosition((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 180.0F, 20.0F);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
import net.minecraft.entity.ai.EntityAISwimming;
|
||||
import net.minecraft.entity.ai.EntityAIWander;
|
||||
import net.minecraft.entity.ai.EntityAIWatchClosest;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityVindicator extends AbstractIllager
|
||||
{
|
||||
private boolean johnny;
|
||||
private static final Predicate<Entity> JOHNNY_SELECTOR = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ instanceof EntityLivingBase && ((EntityLivingBase)p_apply_1_).attackable();
|
||||
}
|
||||
};
|
||||
|
||||
public EntityVindicator(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.95F);
|
||||
}
|
||||
|
||||
public static void registerFixesVindicator(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityVindicator.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
super.initEntityAI();
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false));
|
||||
this.tasks.addTask(8, new EntityAIWander(this, 0.6D));
|
||||
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F));
|
||||
this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[] {EntityVindicator.class}));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityVillager.class, true));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, true));
|
||||
this.targetTasks.addTask(4, new EntityVindicator.AIJohnnyAttack(this));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.3499999940395355D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(12.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(5.0D);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
}
|
||||
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_VINDICATION_ILLAGER;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isAggressive()
|
||||
{
|
||||
return this.isAggressive(1);
|
||||
}
|
||||
|
||||
public void setAggressive(boolean p_190636_1_)
|
||||
{
|
||||
this.setAggressive(1, p_190636_1_);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
|
||||
if (this.johnny)
|
||||
{
|
||||
compound.setBoolean("Johnny", true);
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public AbstractIllager.IllagerArmPose getArmPose()
|
||||
{
|
||||
return this.isAggressive() ? AbstractIllager.IllagerArmPose.ATTACKING : AbstractIllager.IllagerArmPose.CROSSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
|
||||
if (compound.hasKey("Johnny", 99))
|
||||
{
|
||||
this.johnny = compound.getBoolean("Johnny");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
IEntityLivingData ientitylivingdata = super.onInitialSpawn(difficulty, livingdata);
|
||||
this.setEquipmentBasedOnDifficulty(difficulty);
|
||||
this.setEnchantmentBasedOnDifficulty(difficulty);
|
||||
return ientitylivingdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_AXE));
|
||||
}
|
||||
|
||||
protected void updateAITasks()
|
||||
{
|
||||
super.updateAITasks();
|
||||
this.setAggressive(this.getAttackTarget() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Entity is on the same team as the given Entity.
|
||||
*/
|
||||
public boolean isOnSameTeam(Entity entityIn)
|
||||
{
|
||||
if (super.isOnSameTeam(entityIn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (entityIn instanceof EntityLivingBase && ((EntityLivingBase)entityIn).getCreatureAttribute() == EnumCreatureAttribute.ILLAGER)
|
||||
{
|
||||
return this.getTeam() == null && entityIn.getTeam() == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom name tag for this entity
|
||||
*/
|
||||
public void setCustomNameTag(String name)
|
||||
{
|
||||
super.setCustomNameTag(name);
|
||||
|
||||
if (!this.johnny && "Johnny".equals(name))
|
||||
{
|
||||
this.johnny = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.VINDICATION_ILLAGER_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.VINDICATION_ILLAGER_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_VINDICATION_ILLAGER_HURT;
|
||||
}
|
||||
|
||||
static class AIJohnnyAttack extends EntityAINearestAttackableTarget<EntityLivingBase>
|
||||
{
|
||||
public AIJohnnyAttack(EntityVindicator vindicator)
|
||||
{
|
||||
super(vindicator, EntityLivingBase.class, 0, true, true, EntityVindicator.JOHNNY_SELECTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return ((EntityVindicator)this.taskOwner).johnny && super.shouldExecute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIAttackRanged;
|
||||
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.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityPotion;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.PotionTypes;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.potion.PotionType;
|
||||
import net.minecraft.potion.PotionUtils;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityWitch extends EntityMob implements IRangedAttackMob
|
||||
{
|
||||
private static final UUID MODIFIER_UUID = UUID.fromString("5CD17E52-A79A-43D3-A529-90FDE04B181E");
|
||||
private static final AttributeModifier MODIFIER = (new AttributeModifier(MODIFIER_UUID, "Drinking speed penalty", -0.25D, 0)).setSaved(false);
|
||||
private static final DataParameter<Boolean> IS_DRINKING = EntityDataManager.<Boolean>createKey(EntityWitch.class, DataSerializers.BOOLEAN);
|
||||
/**
|
||||
* A timer that counts down until a witch finishes drinking a potion, at which time the held item (if it is a
|
||||
* potion) will have its effects applied. Set to {@link ItemStack#getMaxItemUseDuration()}.
|
||||
*/
|
||||
private int potionUseTimer;
|
||||
|
||||
public EntityWitch(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.95F);
|
||||
}
|
||||
|
||||
public static void registerFixesWitch(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityWitch.class);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(1, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIAttackRanged(this, 1.0D, 60, 10.0F));
|
||||
this.tasks.addTask(2, new EntityAIWanderAvoidWater(this, 1.0D));
|
||||
this.tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(3, new EntityAILookIdle(this));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0]));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.getDataManager().register(IS_DRINKING, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITCH_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_WITCH_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITCH_DEATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this witch is aggressive at an entity.
|
||||
*/
|
||||
public void setDrinkingPotion(boolean drinkingPotion)
|
||||
{
|
||||
this.getDataManager().set(IS_DRINKING, Boolean.valueOf(drinkingPotion));
|
||||
}
|
||||
|
||||
public boolean isDrinkingPotion()
|
||||
{
|
||||
return ((Boolean)this.getDataManager().get(IS_DRINKING)).booleanValue();
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(26.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
if (this.isDrinkingPotion())
|
||||
{
|
||||
if (this.potionUseTimer-- <= 0)
|
||||
{
|
||||
this.setDrinkingPotion(false);
|
||||
ItemStack itemstack = this.getHeldItemMainhand();
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, ItemStack.EMPTY);
|
||||
|
||||
if (itemstack.getItem() == Items.POTIONITEM)
|
||||
{
|
||||
List<PotionEffect> list = PotionUtils.getEffectsFromStack(itemstack);
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
for (PotionEffect potioneffect : list)
|
||||
{
|
||||
this.addPotionEffect(new PotionEffect(potioneffect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).removeModifier(MODIFIER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PotionType potiontype = null;
|
||||
|
||||
if (this.rand.nextFloat() < 0.15F && this.isInsideOfMaterial(Material.WATER) && !this.isPotionActive(MobEffects.WATER_BREATHING))
|
||||
{
|
||||
potiontype = PotionTypes.WATER_BREATHING;
|
||||
}
|
||||
else if (this.rand.nextFloat() < 0.15F && (this.isBurning() || this.getLastDamageSource() != null && this.getLastDamageSource().isFireDamage()) && !this.isPotionActive(MobEffects.FIRE_RESISTANCE))
|
||||
{
|
||||
potiontype = PotionTypes.FIRE_RESISTANCE;
|
||||
}
|
||||
else if (this.rand.nextFloat() < 0.05F && this.getHealth() < this.getMaxHealth())
|
||||
{
|
||||
potiontype = PotionTypes.HEALING;
|
||||
}
|
||||
else if (this.rand.nextFloat() < 0.5F && this.getAttackTarget() != null && !this.isPotionActive(MobEffects.SPEED) && this.getAttackTarget().getDistanceSq(this) > 121.0D)
|
||||
{
|
||||
potiontype = PotionTypes.SWIFTNESS;
|
||||
}
|
||||
|
||||
if (potiontype != null)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, PotionUtils.addPotionToItemStack(new ItemStack(Items.POTIONITEM), potiontype));
|
||||
this.potionUseTimer = this.getHeldItemMainhand().getMaxItemUseDuration();
|
||||
this.setDrinkingPotion(true);
|
||||
this.world.playSound((EntityPlayer)null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_WITCH_DRINK, this.getSoundCategory(), 1.0F, 0.8F + this.rand.nextFloat() * 0.4F);
|
||||
IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
|
||||
iattributeinstance.removeModifier(MODIFIER);
|
||||
iattributeinstance.applyModifier(MODIFIER);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.rand.nextFloat() < 7.5E-4F)
|
||||
{
|
||||
this.world.setEntityState(this, (byte)15);
|
||||
}
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link World#setEntityState}
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void handleStatusUpdate(byte id)
|
||||
{
|
||||
if (id == 15)
|
||||
{
|
||||
for (int i = 0; i < this.rand.nextInt(35) + 10; ++i)
|
||||
{
|
||||
this.world.spawnParticle(EnumParticleTypes.SPELL_WITCH, this.posX + this.rand.nextGaussian() * 0.12999999523162842D, this.getEntityBoundingBox().maxY + 0.5D + this.rand.nextGaussian() * 0.12999999523162842D, this.posZ + this.rand.nextGaussian() * 0.12999999523162842D, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.handleStatusUpdate(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces damage, depending on potions
|
||||
*/
|
||||
protected float applyPotionDamageCalculations(DamageSource source, float damage)
|
||||
{
|
||||
damage = super.applyPotionDamageCalculations(source, damage);
|
||||
|
||||
if (source.getTrueSource() == this)
|
||||
{
|
||||
damage = 0.0F;
|
||||
}
|
||||
|
||||
if (source.isMagicDamage())
|
||||
{
|
||||
damage = (float)((double)damage * 0.15D);
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_WITCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attack the specified entity using a ranged attack.
|
||||
*/
|
||||
public void attackEntityWithRangedAttack(EntityLivingBase target, float distanceFactor)
|
||||
{
|
||||
if (!this.isDrinkingPotion())
|
||||
{
|
||||
double d0 = target.posY + (double)target.getEyeHeight() - 1.100000023841858D;
|
||||
double d1 = target.posX + target.motionX - this.posX;
|
||||
double d2 = d0 - this.posY;
|
||||
double d3 = target.posZ + target.motionZ - this.posZ;
|
||||
float f = MathHelper.sqrt(d1 * d1 + d3 * d3);
|
||||
PotionType potiontype = PotionTypes.HARMING;
|
||||
|
||||
if (f >= 8.0F && !target.isPotionActive(MobEffects.SLOWNESS))
|
||||
{
|
||||
potiontype = PotionTypes.SLOWNESS;
|
||||
}
|
||||
else if (target.getHealth() >= 8.0F && !target.isPotionActive(MobEffects.POISON))
|
||||
{
|
||||
potiontype = PotionTypes.POISON;
|
||||
}
|
||||
else if (f <= 3.0F && !target.isPotionActive(MobEffects.WEAKNESS) && this.rand.nextFloat() < 0.25F)
|
||||
{
|
||||
potiontype = PotionTypes.WEAKNESS;
|
||||
}
|
||||
|
||||
EntityPotion entitypotion = new EntityPotion(this.world, this, PotionUtils.addPotionToItemStack(new ItemStack(Items.SPLASH_POTION), potiontype));
|
||||
entitypotion.rotationPitch -= -20.0F;
|
||||
entitypotion.shoot(d1, d2 + (double)(f * 0.2F), d3, 0.75F, 8.0F);
|
||||
this.world.playSound((EntityPlayer)null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_WITCH_THROW, this.getSoundCategory(), 1.0F, 0.8F + this.rand.nextFloat() * 0.4F);
|
||||
this.world.spawnEntity(entitypotion);
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 1.62F;
|
||||
}
|
||||
|
||||
public void setSwingingArms(boolean swingingArms)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
|
||||
public class EntityWitherSkeleton extends AbstractSkeleton
|
||||
{
|
||||
public EntityWitherSkeleton(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.7F, 2.4F);
|
||||
this.isImmuneToFire = true;
|
||||
}
|
||||
|
||||
public static void registerFixesWitherSkeleton(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityWitherSkeleton.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_WITHER_SKELETON;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_SKELETON_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_SKELETON_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_SKELETON_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_WITHER_SKELETON_STEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mob's health reaches 0.
|
||||
*/
|
||||
public void onDeath(DamageSource cause)
|
||||
{
|
||||
super.onDeath(cause);
|
||||
|
||||
if (cause.getTrueSource() instanceof EntityCreeper)
|
||||
{
|
||||
EntityCreeper entitycreeper = (EntityCreeper)cause.getTrueSource();
|
||||
|
||||
if (entitycreeper.getPowered() && entitycreeper.ableToCauseSkullDrop())
|
||||
{
|
||||
entitycreeper.incrementDroppedSkulls();
|
||||
this.entityDropItem(new ItemStack(Items.SKULL, 1, 1), 0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.STONE_SWORD));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enchants Entity's current equipments based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEnchantmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
IEntityLivingData ientitylivingdata = super.onInitialSpawn(difficulty, livingdata);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(4.0D);
|
||||
this.setCombatTask();
|
||||
return ientitylivingdata;
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
return 2.1F;
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
if (!super.attackEntityAsMob(entityIn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
((EntityLivingBase)entityIn).addPotionEffect(new PotionEffect(MobEffects.WITHER, 200));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected EntityArrow getArrow(float p_190726_1_)
|
||||
{
|
||||
EntityArrow entityarrow = super.getArrow(p_190726_1_);
|
||||
entityarrow.setFire(100);
|
||||
return entityarrow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,639 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.EntityAIBreakDoor;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAILookIdle;
|
||||
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
|
||||
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
|
||||
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.ai.EntityAIZombieAttack;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttribute;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.ai.attributes.RangedAttribute;
|
||||
import net.minecraft.entity.passive.EntityChicken;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.util.DamageSource;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.DifficultyInstance;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityZombie extends EntityMob
|
||||
{
|
||||
/** The attribute which determines the chance that this mob will spawn reinforcements */
|
||||
protected static final IAttribute SPAWN_REINFORCEMENTS_CHANCE = (new RangedAttribute((IAttribute)null, "zombie.spawnReinforcements", 0.0D, 0.0D, 1.0D)).setDescription("Spawn Reinforcements Chance");
|
||||
private static final UUID BABY_SPEED_BOOST_ID = UUID.fromString("B9766B59-9566-4402-BC1F-2EE2A276D836");
|
||||
private static final AttributeModifier BABY_SPEED_BOOST = new AttributeModifier(BABY_SPEED_BOOST_ID, "Baby speed boost", 0.5D, 1);
|
||||
private static final DataParameter<Boolean> IS_CHILD = EntityDataManager.<Boolean>createKey(EntityZombie.class, DataSerializers.BOOLEAN);
|
||||
/**
|
||||
* Was the type of villager for zombie villagers prior to 1.11. Now unused. Use {@link
|
||||
* EntityZombieVillager#PROFESSION} instead.
|
||||
*/
|
||||
private static final DataParameter<Integer> VILLAGER_TYPE = EntityDataManager.<Integer>createKey(EntityZombie.class, DataSerializers.VARINT);
|
||||
private static final DataParameter<Boolean> ARMS_RAISED = EntityDataManager.<Boolean>createKey(EntityZombie.class, DataSerializers.BOOLEAN);
|
||||
private final EntityAIBreakDoor breakDoor = new EntityAIBreakDoor(this);
|
||||
private boolean isBreakDoorsTaskSet;
|
||||
/** The width of the entity */
|
||||
private float zombieWidth = -1.0F;
|
||||
/** The height of the the entity. */
|
||||
private float zombieHeight;
|
||||
|
||||
public EntityZombie(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
this.setSize(0.6F, 1.95F);
|
||||
}
|
||||
|
||||
protected void initEntityAI()
|
||||
{
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIZombieAttack(this, 1.0D, false));
|
||||
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
|
||||
this.tasks.addTask(7, new EntityAIWanderAvoidWater(this, 1.0D));
|
||||
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
this.applyEntityAI();
|
||||
}
|
||||
|
||||
protected void applyEntityAI()
|
||||
{
|
||||
this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false));
|
||||
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, new Class[] {EntityPigZombie.class}));
|
||||
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityVillager.class, false));
|
||||
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, true));
|
||||
}
|
||||
|
||||
protected void applyEntityAttributes()
|
||||
{
|
||||
super.applyEntityAttributes();
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(35.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.23000000417232513D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(3.0D);
|
||||
this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue(2.0D);
|
||||
this.getAttributeMap().registerAttribute(SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.rand.nextDouble() * net.minecraftforge.common.ForgeModContainer.zombieSummonBaseChance);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.getDataManager().register(IS_CHILD, Boolean.valueOf(false));
|
||||
this.getDataManager().register(VILLAGER_TYPE, Integer.valueOf(0));
|
||||
this.getDataManager().register(ARMS_RAISED, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
public void setArmsRaised(boolean armsRaised)
|
||||
{
|
||||
this.getDataManager().set(ARMS_RAISED, Boolean.valueOf(armsRaised));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isArmsRaised()
|
||||
{
|
||||
return ((Boolean)this.getDataManager().get(ARMS_RAISED)).booleanValue();
|
||||
}
|
||||
|
||||
public boolean isBreakDoorsTaskSet()
|
||||
{
|
||||
return this.isBreakDoorsTaskSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or removes EntityAIBreakDoor task
|
||||
*/
|
||||
public void setBreakDoorsAItask(boolean enabled)
|
||||
{
|
||||
if (this.isBreakDoorsTaskSet != enabled)
|
||||
{
|
||||
this.isBreakDoorsTaskSet = enabled;
|
||||
((PathNavigateGround)this.getNavigator()).setBreakDoors(enabled);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
this.tasks.addTask(1, this.breakDoor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tasks.removeTask(this.breakDoor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If Animal, checks if the age timer is negative
|
||||
*/
|
||||
public boolean isChild()
|
||||
{
|
||||
return ((Boolean)this.getDataManager().get(IS_CHILD)).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the experience points the entity currently has.
|
||||
*/
|
||||
protected int getExperiencePoints(EntityPlayer player)
|
||||
{
|
||||
if (this.isChild())
|
||||
{
|
||||
this.experienceValue = (int)((float)this.experienceValue * 2.5F);
|
||||
}
|
||||
|
||||
return super.getExperiencePoints(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this zombie is a child.
|
||||
*/
|
||||
public void setChild(boolean childZombie)
|
||||
{
|
||||
this.getDataManager().set(IS_CHILD, Boolean.valueOf(childZombie));
|
||||
|
||||
if (this.world != null && !this.world.isRemote)
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
|
||||
iattributeinstance.removeModifier(BABY_SPEED_BOOST);
|
||||
|
||||
if (childZombie)
|
||||
{
|
||||
iattributeinstance.applyModifier(BABY_SPEED_BOOST);
|
||||
}
|
||||
}
|
||||
|
||||
this.setChildSize(childZombie);
|
||||
}
|
||||
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
if (IS_CHILD.equals(key))
|
||||
{
|
||||
this.setChildSize(this.isChild());
|
||||
}
|
||||
|
||||
super.notifyDataManagerChange(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (this.world.isDaytime() && !this.world.isRemote && !this.isChild() && this.shouldBurnInDay())
|
||||
{
|
||||
float f = this.getBrightness();
|
||||
|
||||
if (f > 0.5F && this.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.world.canSeeSky(new BlockPos(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ)))
|
||||
{
|
||||
boolean flag = true;
|
||||
ItemStack itemstack = this.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
|
||||
|
||||
if (!itemstack.isEmpty())
|
||||
{
|
||||
if (itemstack.isItemStackDamageable())
|
||||
{
|
||||
itemstack.setItemDamage(itemstack.getItemDamage() + this.rand.nextInt(2));
|
||||
|
||||
if (itemstack.getItemDamage() >= itemstack.getMaxDamage())
|
||||
{
|
||||
this.renderBrokenItemStack(itemstack);
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.setFire(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.onLivingUpdate();
|
||||
}
|
||||
|
||||
protected boolean shouldBurnInDay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity is attacked.
|
||||
*/
|
||||
public boolean attackEntityFrom(DamageSource source, float amount)
|
||||
{
|
||||
if (super.attackEntityFrom(source, amount))
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null && source.getTrueSource() instanceof EntityLivingBase)
|
||||
{
|
||||
entitylivingbase = (EntityLivingBase)source.getTrueSource();
|
||||
}
|
||||
|
||||
int i = MathHelper.floor(this.posX);
|
||||
int j = MathHelper.floor(this.posY);
|
||||
int k = MathHelper.floor(this.posZ);
|
||||
net.minecraftforge.event.entity.living.ZombieEvent.SummonAidEvent summonAid = net.minecraftforge.event.ForgeEventFactory.fireZombieSummonAid(this, world, i, j, k, entitylivingbase, this.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).getAttributeValue());
|
||||
if (summonAid.getResult() == net.minecraftforge.fml.common.eventhandler.Event.Result.DENY) return true;
|
||||
|
||||
if (summonAid.getResult() == net.minecraftforge.fml.common.eventhandler.Event.Result.ALLOW ||
|
||||
entitylivingbase != null && this.world.getDifficulty() == EnumDifficulty.HARD && (double)this.rand.nextFloat() < this.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).getAttributeValue() && this.world.getGameRules().getBoolean("doMobSpawning"))
|
||||
{
|
||||
EntityZombie entityzombie;
|
||||
if (summonAid.getCustomSummonedAid() != null && summonAid.getResult() == net.minecraftforge.fml.common.eventhandler.Event.Result.ALLOW)
|
||||
{
|
||||
entityzombie = summonAid.getCustomSummonedAid();
|
||||
}
|
||||
else
|
||||
{
|
||||
entityzombie = new EntityZombie(this.world);
|
||||
}
|
||||
|
||||
for (int l = 0; l < 50; ++l)
|
||||
{
|
||||
int i1 = i + MathHelper.getInt(this.rand, 7, 40) * MathHelper.getInt(this.rand, -1, 1);
|
||||
int j1 = j + MathHelper.getInt(this.rand, 7, 40) * MathHelper.getInt(this.rand, -1, 1);
|
||||
int k1 = k + MathHelper.getInt(this.rand, 7, 40) * MathHelper.getInt(this.rand, -1, 1);
|
||||
|
||||
if (this.world.getBlockState(new BlockPos(i1, j1 - 1, k1)).isSideSolid(this.world, new BlockPos(i1, j1 - 1, k1), net.minecraft.util.EnumFacing.UP) && this.world.getLightFromNeighbors(new BlockPos(i1, j1, k1)) < 10)
|
||||
{
|
||||
entityzombie.setPosition((double)i1, (double)j1, (double)k1);
|
||||
|
||||
if (!this.world.isAnyPlayerWithinRangeAt((double)i1, (double)j1, (double)k1, 7.0D) && this.world.checkNoEntityCollision(entityzombie.getEntityBoundingBox(), entityzombie) && this.world.getCollisionBoxes(entityzombie, entityzombie.getEntityBoundingBox()).isEmpty() && !this.world.containsAnyLiquid(entityzombie.getEntityBoundingBox()))
|
||||
{
|
||||
this.world.spawnEntity(entityzombie);
|
||||
if (entitylivingbase != null) entityzombie.setAttackTarget(entitylivingbase);
|
||||
entityzombie.onInitialSpawn(this.world.getDifficultyForLocation(new BlockPos(entityzombie)), (IEntityLivingData)null);
|
||||
this.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).applyModifier(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, 0));
|
||||
entityzombie.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).applyModifier(new AttributeModifier("Zombie reinforcement callee charge", -0.05000000074505806D, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean attackEntityAsMob(Entity entityIn)
|
||||
{
|
||||
boolean flag = super.attackEntityAsMob(entityIn);
|
||||
|
||||
if (flag)
|
||||
{
|
||||
float f = this.world.getDifficultyForLocation(new BlockPos(this)).getAdditionalDifficulty();
|
||||
|
||||
if (this.getHeldItemMainhand().isEmpty() && this.isBurning() && this.rand.nextFloat() < f * 0.3F)
|
||||
{
|
||||
entityIn.setFire(2 * (int)f);
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
protected SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_AMBIENT;
|
||||
}
|
||||
|
||||
protected SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_HURT;
|
||||
}
|
||||
|
||||
protected SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_DEATH;
|
||||
}
|
||||
|
||||
protected SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_STEP;
|
||||
}
|
||||
|
||||
protected void playStepSound(BlockPos pos, Block blockIn)
|
||||
{
|
||||
this.playSound(this.getStepSound(), 0.15F, 1.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Entity's EnumCreatureAttribute
|
||||
*/
|
||||
public EnumCreatureAttribute getCreatureAttribute()
|
||||
{
|
||||
return EnumCreatureAttribute.UNDEAD;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ZOMBIE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives armor or weapon for entity based on given DifficultyInstance
|
||||
*/
|
||||
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty)
|
||||
{
|
||||
super.setEquipmentBasedOnDifficulty(difficulty);
|
||||
|
||||
if (this.rand.nextFloat() < (this.world.getDifficulty() == EnumDifficulty.HARD ? 0.05F : 0.01F))
|
||||
{
|
||||
int i = this.rand.nextInt(3);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SHOVEL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerFixesZombie(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityZombie.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
|
||||
if (this.isChild())
|
||||
{
|
||||
compound.setBoolean("IsBaby", true);
|
||||
}
|
||||
|
||||
compound.setBoolean("CanBreakDoors", this.isBreakDoorsTaskSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
|
||||
if (compound.getBoolean("IsBaby"))
|
||||
{
|
||||
this.setChild(true);
|
||||
}
|
||||
|
||||
this.setBreakDoorsAItask(compound.getBoolean("CanBreakDoors"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called when the entity kills another one.
|
||||
*/
|
||||
public void onKillEntity(EntityLivingBase entityLivingIn)
|
||||
{
|
||||
super.onKillEntity(entityLivingIn);
|
||||
|
||||
if ((this.world.getDifficulty() == EnumDifficulty.NORMAL || this.world.getDifficulty() == EnumDifficulty.HARD) && entityLivingIn instanceof EntityVillager)
|
||||
{
|
||||
if (this.world.getDifficulty() != EnumDifficulty.HARD && this.rand.nextBoolean())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EntityVillager entityvillager = (EntityVillager)entityLivingIn;
|
||||
EntityZombieVillager entityzombievillager = new EntityZombieVillager(this.world);
|
||||
entityzombievillager.copyLocationAndAnglesFrom(entityvillager);
|
||||
this.world.removeEntity(entityvillager);
|
||||
entityzombievillager.onInitialSpawn(this.world.getDifficultyForLocation(new BlockPos(entityzombievillager)), new EntityZombie.GroupData(false));
|
||||
entityzombievillager.setProfession(entityvillager.getProfession());
|
||||
entityzombievillager.setChild(entityvillager.isChild());
|
||||
entityzombievillager.setNoAI(entityvillager.isAIDisabled());
|
||||
|
||||
if (entityvillager.hasCustomName())
|
||||
{
|
||||
entityzombievillager.setCustomNameTag(entityvillager.getCustomNameTag());
|
||||
entityzombievillager.setAlwaysRenderNameTag(entityvillager.getAlwaysRenderNameTag());
|
||||
}
|
||||
|
||||
this.world.spawnEntity(entityzombievillager);
|
||||
this.world.playEvent((EntityPlayer)null, 1026, new BlockPos(this), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public float getEyeHeight()
|
||||
{
|
||||
float f = 1.74F;
|
||||
|
||||
if (this.isChild())
|
||||
{
|
||||
f = (float)((double)f - 0.81D);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
protected boolean canEquipItem(ItemStack stack)
|
||||
{
|
||||
return stack.getItem() == Items.EGG && this.isChild() && this.isRiding() ? false : super.canEquipItem(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
livingdata = super.onInitialSpawn(difficulty, livingdata);
|
||||
float f = difficulty.getClampedAdditionalDifficulty();
|
||||
this.setCanPickUpLoot(this.rand.nextFloat() < 0.55F * f);
|
||||
|
||||
if (livingdata == null)
|
||||
{
|
||||
livingdata = new EntityZombie.GroupData(this.world.rand.nextFloat() < net.minecraftforge.common.ForgeModContainer.zombieBabyChance);
|
||||
}
|
||||
|
||||
if (livingdata instanceof EntityZombie.GroupData)
|
||||
{
|
||||
EntityZombie.GroupData entityzombie$groupdata = (EntityZombie.GroupData)livingdata;
|
||||
|
||||
if (entityzombie$groupdata.isChild)
|
||||
{
|
||||
this.setChild(true);
|
||||
|
||||
if ((double)this.world.rand.nextFloat() < 0.05D)
|
||||
{
|
||||
List<EntityChicken> list = this.world.<EntityChicken>getEntitiesWithinAABB(EntityChicken.class, this.getEntityBoundingBox().grow(5.0D, 3.0D, 5.0D), EntitySelectors.IS_STANDALONE);
|
||||
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
EntityChicken entitychicken = list.get(0);
|
||||
entitychicken.setChickenJockey(true);
|
||||
this.startRiding(entitychicken);
|
||||
}
|
||||
}
|
||||
else if ((double)this.world.rand.nextFloat() < 0.05D)
|
||||
{
|
||||
EntityChicken entitychicken1 = new EntityChicken(this.world);
|
||||
entitychicken1.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, 0.0F);
|
||||
entitychicken1.onInitialSpawn(difficulty, (IEntityLivingData)null);
|
||||
entitychicken1.setChickenJockey(true);
|
||||
this.world.spawnEntity(entitychicken1);
|
||||
this.startRiding(entitychicken1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setBreakDoorsAItask(this.rand.nextFloat() < f * 0.1F);
|
||||
this.setEquipmentBasedOnDifficulty(difficulty);
|
||||
this.setEnchantmentBasedOnDifficulty(difficulty);
|
||||
|
||||
if (this.getItemStackFromSlot(EntityEquipmentSlot.HEAD).isEmpty())
|
||||
{
|
||||
Calendar calendar = this.world.getCurrentDate();
|
||||
|
||||
if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F)
|
||||
{
|
||||
this.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN));
|
||||
this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
this.getEntityAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextDouble() * 0.05000000074505806D, 0));
|
||||
double d0 = this.rand.nextDouble() * 1.5D * (double)f;
|
||||
|
||||
if (d0 > 1.0D)
|
||||
{
|
||||
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random zombie-spawn bonus", d0, 2));
|
||||
}
|
||||
|
||||
if (this.rand.nextFloat() < f * 0.05F)
|
||||
{
|
||||
this.getEntityAttribute(SPAWN_REINFORCEMENTS_CHANCE).applyModifier(new AttributeModifier("Leader zombie bonus", this.rand.nextDouble() * 0.25D + 0.5D, 0));
|
||||
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).applyModifier(new AttributeModifier("Leader zombie bonus", this.rand.nextDouble() * 3.0D + 1.0D, 2));
|
||||
this.setBreakDoorsAItask(true);
|
||||
}
|
||||
|
||||
return livingdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the size of the entity to be half of its current size if true.
|
||||
*/
|
||||
public void setChildSize(boolean isChild)
|
||||
{
|
||||
this.multiplySize(isChild ? 0.5F : 1.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width and height of the entity.
|
||||
*/
|
||||
protected final void setSize(float width, float height)
|
||||
{
|
||||
boolean flag = this.zombieWidth > 0.0F && this.zombieHeight > 0.0F;
|
||||
this.zombieWidth = width;
|
||||
this.zombieHeight = height;
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
this.multiplySize(1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the height and width by the provided float.
|
||||
*/
|
||||
protected final void multiplySize(float size)
|
||||
{
|
||||
super.setSize(this.zombieWidth * size, this.zombieHeight * size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y Offset of this entity.
|
||||
*/
|
||||
public double getYOffset()
|
||||
{
|
||||
return this.isChild() ? 0.0D : -0.45D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mob's health reaches 0.
|
||||
*/
|
||||
public void onDeath(DamageSource cause)
|
||||
{
|
||||
super.onDeath(cause);
|
||||
|
||||
if (cause.getTrueSource() instanceof EntityCreeper)
|
||||
{
|
||||
EntityCreeper entitycreeper = (EntityCreeper)cause.getTrueSource();
|
||||
|
||||
if (entitycreeper.getPowered() && entitycreeper.ableToCauseSkullDrop())
|
||||
{
|
||||
entitycreeper.incrementDroppedSkulls();
|
||||
ItemStack itemstack = this.getSkullDrop();
|
||||
|
||||
if (!itemstack.isEmpty())
|
||||
{
|
||||
this.entityDropItem(itemstack, 0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ItemStack getSkullDrop()
|
||||
{
|
||||
return new ItemStack(Items.SKULL, 1, 2);
|
||||
}
|
||||
|
||||
class GroupData implements IEntityLivingData
|
||||
{
|
||||
public boolean isChild;
|
||||
|
||||
private GroupData(boolean p_i47328_2_)
|
||||
{
|
||||
this.isChild = p_i47328_2_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.advancements.CriteriaTriggers;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootTableList;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class EntityZombieVillager extends EntityZombie
|
||||
{
|
||||
private static final DataParameter<Boolean> CONVERTING = EntityDataManager.<Boolean>createKey(EntityZombieVillager.class, DataSerializers.BOOLEAN);
|
||||
private static final DataParameter<Integer> PROFESSION = EntityDataManager.<Integer>createKey(EntityZombieVillager.class, DataSerializers.VARINT);
|
||||
/** Ticker used to determine the time remaining for this zombie to convert into a villager when cured. */
|
||||
private int conversionTime;
|
||||
/**
|
||||
* The entity that started the conversion, used for the {@link CriteriaTriggers#CURED_ZOMBIE_VILLAGER} advancement
|
||||
* criteria
|
||||
*/
|
||||
private UUID converstionStarter;
|
||||
|
||||
public EntityZombieVillager(World worldIn)
|
||||
{
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
protected void entityInit()
|
||||
{
|
||||
super.entityInit();
|
||||
this.dataManager.register(CONVERTING, Boolean.valueOf(false));
|
||||
this.dataManager.register(PROFESSION, Integer.valueOf(0));
|
||||
}
|
||||
|
||||
public void setProfession(int profession)
|
||||
{
|
||||
this.dataManager.set(PROFESSION, Integer.valueOf(profession));
|
||||
net.minecraftforge.fml.common.registry.VillagerRegistry.onSetProfession(this, profession);
|
||||
}
|
||||
|
||||
//Use Forge Variant below
|
||||
@Deprecated
|
||||
public int getProfession()
|
||||
{
|
||||
return Math.max(((Integer)this.dataManager.get(PROFESSION)).intValue(), 0);
|
||||
}
|
||||
|
||||
public static void registerFixesZombieVillager(DataFixer fixer)
|
||||
{
|
||||
EntityLiving.registerFixesMob(fixer, EntityZombieVillager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to write subclass entity data to NBT.
|
||||
*/
|
||||
public void writeEntityToNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.writeEntityToNBT(compound);
|
||||
compound.setInteger("Profession", this.getProfession());
|
||||
compound.setString("ProfessionName", this.getForgeProfession().getRegistryName().toString());
|
||||
compound.setInteger("ConversionTime", this.isConverting() ? this.conversionTime : -1);
|
||||
|
||||
if (this.converstionStarter != null)
|
||||
{
|
||||
compound.setUniqueId("ConversionPlayer", this.converstionStarter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (abstract) Protected helper method to read subclass entity data from NBT.
|
||||
*/
|
||||
public void readEntityFromNBT(NBTTagCompound compound)
|
||||
{
|
||||
super.readEntityFromNBT(compound);
|
||||
this.setProfession(compound.getInteger("Profession"));
|
||||
if (compound.hasKey("ProfessionName"))
|
||||
{
|
||||
net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession p = net.minecraftforge.fml.common.registry.ForgeRegistries.VILLAGER_PROFESSIONS.getValue(new net.minecraft.util.ResourceLocation(compound.getString("ProfessionName")));
|
||||
if (p == null) p = net.minecraftforge.fml.common.registry.VillagerRegistry.FARMER;
|
||||
this.setForgeProfession(p);
|
||||
}
|
||||
|
||||
if (compound.hasKey("ConversionTime", 99) && compound.getInteger("ConversionTime") > -1)
|
||||
{
|
||||
this.startConverting(compound.hasUniqueId("ConversionPlayer") ? compound.getUniqueId("ConversionPlayer") : null, compound.getInteger("ConversionTime"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
|
||||
* when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
|
||||
*/
|
||||
@Nullable
|
||||
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
|
||||
{
|
||||
this.setProfession(this.world.rand.nextInt(6));
|
||||
return super.onInitialSpawn(difficulty, livingdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
if (!this.world.isRemote && this.isConverting())
|
||||
{
|
||||
int i = this.getConversionProgress();
|
||||
this.conversionTime -= i;
|
||||
|
||||
if (this.conversionTime <= 0)
|
||||
{
|
||||
this.finishConversion();
|
||||
}
|
||||
}
|
||||
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
public boolean processInteract(EntityPlayer player, EnumHand hand)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItem(hand);
|
||||
|
||||
if (itemstack.getItem() == Items.GOLDEN_APPLE && itemstack.getMetadata() == 0 && this.isPotionActive(MobEffects.WEAKNESS))
|
||||
{
|
||||
if (!player.capabilities.isCreativeMode)
|
||||
{
|
||||
itemstack.shrink(1);
|
||||
}
|
||||
|
||||
if (!this.world.isRemote)
|
||||
{
|
||||
this.startConverting(player.getUniqueID(), this.rand.nextInt(2401) + 3600);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an entity can be despawned, used on idle far away entities
|
||||
*/
|
||||
protected boolean canDespawn()
|
||||
{
|
||||
return !this.isConverting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this zombie is in the process of converting to a villager
|
||||
*/
|
||||
public boolean isConverting()
|
||||
{
|
||||
return ((Boolean)this.getDataManager().get(CONVERTING)).booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts conversion of this zombie villager to a villager
|
||||
*
|
||||
* @param conversionStarterIn The entity that started the conversion's UUID
|
||||
* @param conversionTimeIn The time that it will take to finish conversion
|
||||
*/
|
||||
protected void startConverting(@Nullable UUID conversionStarterIn, int conversionTimeIn)
|
||||
{
|
||||
this.converstionStarter = conversionStarterIn;
|
||||
this.conversionTime = conversionTimeIn;
|
||||
this.getDataManager().set(CONVERTING, Boolean.valueOf(true));
|
||||
this.removePotionEffect(MobEffects.WEAKNESS);
|
||||
this.addPotionEffect(new PotionEffect(MobEffects.STRENGTH, conversionTimeIn, Math.min(this.world.getDifficulty().getDifficultyId() - 1, 0)));
|
||||
this.world.setEntityState(this, (byte)16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link World#setEntityState}
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void handleStatusUpdate(byte id)
|
||||
{
|
||||
if (id == 16)
|
||||
{
|
||||
if (!this.isSilent())
|
||||
{
|
||||
this.world.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, this.getSoundCategory(), 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.handleStatusUpdate(id);
|
||||
}
|
||||
}
|
||||
|
||||
protected void finishConversion()
|
||||
{
|
||||
EntityVillager entityvillager = new EntityVillager(this.world);
|
||||
entityvillager.copyLocationAndAnglesFrom(this);
|
||||
entityvillager.setProfession(this.getForgeProfession());
|
||||
entityvillager.finalizeMobSpawn(this.world.getDifficultyForLocation(new BlockPos(entityvillager)), (IEntityLivingData)null, false);
|
||||
entityvillager.setLookingForHome();
|
||||
|
||||
if (this.isChild())
|
||||
{
|
||||
entityvillager.setGrowingAge(-24000);
|
||||
}
|
||||
|
||||
this.world.removeEntity(this);
|
||||
entityvillager.setNoAI(this.isAIDisabled());
|
||||
|
||||
if (this.hasCustomName())
|
||||
{
|
||||
entityvillager.setCustomNameTag(this.getCustomNameTag());
|
||||
entityvillager.setAlwaysRenderNameTag(this.getAlwaysRenderNameTag());
|
||||
}
|
||||
|
||||
this.world.spawnEntity(entityvillager);
|
||||
|
||||
if (this.converstionStarter != null)
|
||||
{
|
||||
EntityPlayer entityplayer = this.world.getPlayerEntityByUUID(this.converstionStarter);
|
||||
|
||||
if (entityplayer instanceof EntityPlayerMP)
|
||||
{
|
||||
CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((EntityPlayerMP)entityplayer, this, entityvillager);
|
||||
}
|
||||
}
|
||||
|
||||
entityvillager.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 200, 0));
|
||||
this.world.playEvent((EntityPlayer)null, 1027, new BlockPos((int)this.posX, (int)this.posY, (int)this.posZ), 0);
|
||||
}
|
||||
|
||||
protected int getConversionProgress()
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
if (this.rand.nextFloat() < 0.01F)
|
||||
{
|
||||
int j = 0;
|
||||
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int k = (int)this.posX - 4; k < (int)this.posX + 4 && j < 14; ++k)
|
||||
{
|
||||
for (int l = (int)this.posY - 4; l < (int)this.posY + 4 && j < 14; ++l)
|
||||
{
|
||||
for (int i1 = (int)this.posZ - 4; i1 < (int)this.posZ + 4 && j < 14; ++i1)
|
||||
{
|
||||
Block block = this.world.getBlockState(blockpos$mutableblockpos.setPos(k, l, i1)).getBlock();
|
||||
|
||||
if (block == Blocks.IRON_BARS || block == Blocks.BED)
|
||||
{
|
||||
if (this.rand.nextFloat() < 0.3F)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pitch of living sounds in living entities.
|
||||
*/
|
||||
protected float getSoundPitch()
|
||||
{
|
||||
return this.isChild() ? (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 2.0F : (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F;
|
||||
}
|
||||
|
||||
public SoundEvent getAmbientSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_VILLAGER_AMBIENT;
|
||||
}
|
||||
|
||||
public SoundEvent getHurtSound(DamageSource damageSourceIn)
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_VILLAGER_HURT;
|
||||
}
|
||||
|
||||
public SoundEvent getDeathSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_VILLAGER_DEATH;
|
||||
}
|
||||
|
||||
public SoundEvent getStepSound()
|
||||
{
|
||||
return SoundEvents.ENTITY_ZOMBIE_VILLAGER_STEP;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getLootTable()
|
||||
{
|
||||
return LootTableList.ENTITIES_ZOMBIE_VILLAGER;
|
||||
}
|
||||
|
||||
protected ItemStack getSkullDrop()
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
/* ======================================== FORGE START =====================================*/
|
||||
|
||||
@Nullable
|
||||
private net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession prof;
|
||||
public void setForgeProfession(net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession prof)
|
||||
{
|
||||
this.prof = prof;
|
||||
this.setProfession(net.minecraftforge.fml.common.registry.VillagerRegistry.getId(prof));
|
||||
}
|
||||
|
||||
public net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession getForgeProfession()
|
||||
{
|
||||
if (this.prof == null)
|
||||
{
|
||||
this.prof = net.minecraftforge.fml.common.registry.VillagerRegistry.getById(this.getProfession());
|
||||
if (this.prof == null)
|
||||
return net.minecraftforge.fml.common.registry.VillagerRegistry.FARMER;
|
||||
}
|
||||
return this.prof;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataManagerChange(DataParameter<?> key)
|
||||
{
|
||||
super.notifyDataManagerChange(key);
|
||||
if (key.equals(PROFESSION))
|
||||
{
|
||||
net.minecraftforge.fml.common.registry.VillagerRegistry.onSetProfession(this, this.dataManager.get(PROFESSION));
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================== FORGE END =====================================*/
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.passive.IAnimals;
|
||||
|
||||
public interface IMob extends IAnimals
|
||||
{
|
||||
/** Entity selector for IMob types. */
|
||||
Predicate<Entity> MOB_SELECTOR = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ instanceof IMob;
|
||||
}
|
||||
};
|
||||
/** Entity selector for IMob types that are not invisible */
|
||||
Predicate<Entity> VISIBLE_MOB_SELECTOR = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ instanceof IMob && !p_apply_1_.isInvisible();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.entity.monster;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
Reference in New Issue
Block a user